From f81d0d7dc41779a95d164098eabb219a95df5970 Mon Sep 17 00:00:00 2001
From: Marcus Kazmierczak
Date: Wed, 20 Feb 2019 09:54:57 -0800
Subject: [PATCH 001/169] Add ESNext syntax to meta block tutorial (#13954)
* Add ESNext syntax to meta block tutorial
* Applied WordPress code styles to the examples
* Apply suggestions from code review
Co-Authored-By: mkaz
---
.../tutorials/metabox/meta-block-3-add.md | 75 +++++++++++++++----
1 file changed, 60 insertions(+), 15 deletions(-)
diff --git a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
index 0ad9966b91a12..4f6e54f148ed7 100644
--- a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
+++ b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
@@ -10,26 +10,28 @@ By specifying the source of the attributes as `meta`, the Block Editor automatic
Add this code to your JavaScript file (this tutorial will call the file `myguten.js`):
+{% codetabs %}
+{% ES5 %}
```js
( function( wp ) {
var el = wp.element.createElement;
var registerBlockType = wp.blocks.registerBlockType;
- var TextField = wp.components.TextControl;
+ var TextControl = wp.components.TextControl;
- registerBlockType("myguten/meta-block", {
- title: "Meta Block",
- icon: "smiley",
- category: "common",
+ registerBlockType( 'myguten/meta-block', {
+ title: 'Meta Block',
+ icon: 'smiley',
+ category: 'common',
attributes: {
blockValue: {
- type: "string",
- source: "meta",
- meta: "myguten_meta_block_field"
+ type: 'string',
+ source: 'meta',
+ meta: 'myguten_meta_block_field'
}
},
- edit: function(props) {
+ edit: function( props ) {
var className = props.className;
var setAttributes = props.setAttributes;
@@ -37,11 +39,11 @@ Add this code to your JavaScript file (this tutorial will call the file `myguten
setAttributes({ blockValue });
}
- return el(
- "div",
+ return el(
+ 'div',
{ className: className },
- el( TextField, {
- label: "Meta Block Field",
+ el( TextControl, {
+ label: 'Meta Block Field',
value: props.attributes.blockValue,
onChange: updateBlockValue
} )
@@ -53,9 +55,52 @@ Add this code to your JavaScript file (this tutorial will call the file `myguten
save: function() {
return null;
}
- });
-})( window.wp );
+ } );
+} )( window.wp );
```
+{% ESNext %}
+```jsx
+import { registerBlockType } from '@wordpress/blocks';
+import { TextControl } from '@wordpress/components';
+
+registerBlockType( 'myguten/meta-block', {
+ title: 'Meta Block',
+ icon: 'smiley',
+ category: 'common',
+
+ attributes: {
+ blockValue: {
+ type: 'string',
+ source: 'meta',
+ meta: 'myguten_meta_block_field',
+ },
+ },
+
+ edit( { className, setAttributes, attributes } ) {
+
+ function updateBlockValue( blockValue ) {
+ setAttributes( { blockValue } );
+ }
+
+ return (
+
+
+
+ );
+ },
+
+ // No information saved to the block
+ // Data is saved to post meta via attributes
+ save() {
+ return null;
+ }
+} );
+```
+{% end %}
**Important:** Before you test, you need to enqueue your JavaScript file and its dependencies. Note the WordPress packages used above are `wp.element`, `wp.blocks`, and `wp.components`. Each of these need to be included in the array of dependencies. Update the `myguten-meta-block.php` file adding the enqueue function:
From 1020543c18a7e303e652ba5b27c4e434642ec72c Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Wed, 20 Feb 2019 16:00:40 -0500
Subject: [PATCH 002/169] Editor: RichText: Check for presence of inputType
(#13986)
---
packages/editor/src/components/rich-text/index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js
index 1d6c353448d9e..724c9f26e30e6 100644
--- a/packages/editor/src/components/rich-text/index.js
+++ b/packages/editor/src/components/rich-text/index.js
@@ -366,7 +366,7 @@ export class RichText extends Component {
return;
}
- if ( event ) {
+ if ( event && event.nativeEvent.inputType ) {
const { inputType } = event.nativeEvent;
// The browser formatted something or tried to insert HTML.
From 4be0a36c703a45cdcac67807d30e2e4f26c92343 Mon Sep 17 00:00:00 2001
From: Robert Anderson
Date: Thu, 21 Feb 2019 14:29:44 +1100
Subject: [PATCH 003/169] Bump plugin version to 5.1.1 (#13990)
---
gutenberg.php | 2 +-
package-lock.json | 2 +-
package.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/gutenberg.php b/gutenberg.php
index 3ade4ca6d01b0..40da12a8c4b8c 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -3,7 +3,7 @@
* Plugin Name: Gutenberg
* Plugin URI: https://github.com/WordPress/gutenberg
* Description: Printing since 1440. This is the development plugin for the new block editor in core.
- * Version: 5.1.0
+ * Version: 5.1.1
* Author: Gutenberg Team
*
* @package gutenberg
diff --git a/package-lock.json b/package-lock.json
index 55d51e8dce8bb..ed633e96e9bd2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "5.1.0",
+ "version": "5.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 8dd9929cfa023..ea6cd663dbca0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "5.1.0",
+ "version": "5.1.1",
"private": true,
"description": "A new WordPress editor experience",
"repository": "git+https://github.com/WordPress/gutenberg.git",
From c60224004300e06c9875fe78511312f4dd812b9d Mon Sep 17 00:00:00 2001
From: andrei draganescu
Date: Thu, 21 Feb 2019 11:08:55 +0200
Subject: [PATCH 004/169] Added a snippet for observing the browser when
running e2e tests (#13993)
* Added a snippet for observing the browser when running e2e tests
---
docs/contributors/testing-overview.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/docs/contributors/testing-overview.md b/docs/contributors/testing-overview.md
index 7d0d01921a6a5..6992bd664c3ec 100644
--- a/docs/contributors/testing-overview.md
+++ b/docs/contributors/testing-overview.md
@@ -356,6 +356,12 @@ or interactively
npm run test-e2e:watch
```
+Sometimes it's useful to observe the browser while running tests. To do so you can use these environment variables:
+
+```bash
+PUPPETEER_HEADLESS=false PUPPETEER_SLOWMO=80 npm run test-e2e:watch
+```
+
If you're using a different setup, you can provide the base URL, username and password like this:
```bash
From 72ff590394027945b4adea0cd1d838a6060671c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?=
Date: Thu, 21 Feb 2019 11:00:02 +0100
Subject: [PATCH 005/169] Extract reusable part of Webpack config and put in
@wordpress/scripts (#13814)
* New build-config package with webpack config.
Pull the Gutenberg webpack config into a package so it can be re-used for
block/extension development.
* Require new build-config package.
* Dynamically handle WP externals with a function.
Use code from WP Calypso for handling WP externals so we don't have to have the
actual list of packages accessible in our webpack configuration.
* Use webpack config from build-config package.
* Require build-config package.
* Adjust file refs for WP packages.
* Move main gutenberg entry definition and webpack copy plugin out of build-config.
* Add react-dev-utils for formatting webpack compiler messages.
* Implement build script using webpack config from build-config.
* Adjust output path so build goes to working directory.
* Update package name to webpack-config
* Apply more tweaks to the way webpack config package is structured
* Update the way externals are handled
* Add default values for entry and output
* Move shared webpack config under @wordpress/scripts package
* Improve the way how loaders are handled
* Replace GUTENBERG with WP in webpack config env variables
Co-Authored-By: gziolo
* Bring back feature flag to webpack config accidentally removed during merge
* Add missing dev dependencies for the packages used in webpack config
* Fix the list of excluded folders for babel-loader
---
package-lock.json | 90 ++++---
package.json | 5 +-
.../test/index.js | 3 +-
.../CHANGELOG.md | 10 +-
.../block-serialization-spec-parser/index.js | 2 -
.../package.json | 6 +-
.../test/index.js | 3 +-
packages/scripts/config/webpack.config.js | 109 +++++++++
packages/scripts/package.json | 6 +-
packages/scripts/scripts/build.js | 1 -
packages/scripts/utils/index.js | 4 +
packages/scripts/utils/string.js | 20 ++
packages/scripts/utils/test/string.js | 23 ++
webpack.config.js | 228 ++++++------------
14 files changed, 305 insertions(+), 205 deletions(-)
delete mode 100644 packages/block-serialization-spec-parser/index.js
create mode 100644 packages/scripts/config/webpack.config.js
create mode 100644 packages/scripts/utils/string.js
create mode 100644 packages/scripts/utils/test/string.js
diff --git a/package-lock.json b/package-lock.json
index ed633e96e9bd2..656ba9ae3cd0e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2562,7 +2562,10 @@
}
},
"@wordpress/block-serialization-spec-parser": {
- "version": "file:packages/block-serialization-spec-parser"
+ "version": "file:packages/block-serialization-spec-parser",
+ "requires": {
+ "pegjs": "^0.10.0"
+ }
},
"@wordpress/blocks": {
"version": "file:packages/blocks",
@@ -2997,6 +3000,7 @@
"@wordpress/eslint-plugin": "file:packages/eslint-plugin",
"@wordpress/jest-preset-default": "file:packages/jest-preset-default",
"@wordpress/npm-package-json-lint-config": "file:packages/npm-package-json-lint-config",
+ "babel-loader": "^8.0.5",
"chalk": "^2.4.1",
"check-node-version": "^3.1.1",
"cross-spawn": "^5.1.0",
@@ -3007,10 +3011,13 @@
"puppeteer": "1.6.1",
"read-pkg-up": "^1.0.1",
"resolve-bin": "^0.4.0",
+ "source-map-loader": "^0.2.4",
"stylelint": "^9.10.1",
"stylelint-config-wordpress": "^13.1.0",
"webpack": "4.8.3",
- "webpack-cli": "^3.1.2"
+ "webpack-bundle-analyzer": "^3.0.3",
+ "webpack-cli": "^3.1.2",
+ "webpack-livereload-plugin": "^2.2.0"
}
},
"@wordpress/shortcode": {
@@ -15869,8 +15876,7 @@
"pegjs": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz",
- "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=",
- "dev": true
+ "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0="
},
"pend": {
"version": "1.2.0",
@@ -15956,6 +15962,34 @@
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
"dev": true
},
+ "portfinder": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
+ "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==",
+ "dev": true,
+ "requires": {
+ "async": "^1.5.2",
+ "debug": "^2.2.0",
+ "mkdirp": "0.5.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -19763,34 +19797,13 @@
"dev": true
},
"source-map-loader": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.3.tgz",
- "integrity": "sha512-MYbFX9DYxmTQFfy2v8FC1XZwpwHKYxg3SK8Wb7VPBKuhDjz8gi9re2819MsG4p49HDyiOSUKlmZ+nQBArW5CGw==",
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz",
+ "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==",
"dev": true,
"requires": {
"async": "^2.5.0",
- "loader-utils": "~0.2.2",
- "source-map": "~0.6.1"
- },
- "dependencies": {
- "loader-utils": {
- "version": "0.2.17",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
- "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
- "dev": true,
- "requires": {
- "big.js": "^3.1.3",
- "emojis-list": "^2.0.0",
- "json5": "^0.5.0",
- "object-assign": "^4.0.1"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "loader-utils": "^1.1.0"
}
},
"source-map-resolve": {
@@ -21820,9 +21833,9 @@
}
},
"webpack-bundle-analyzer": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.2.tgz",
- "integrity": "sha512-cZG4wSQtKrSpk5RJ33dxiaAyo8bP0V+JvycAyIDFEiDIhw4LHhhVKhn40YT1w6TR9E4scHA00LnIoBtTA13Mow==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz",
+ "integrity": "sha512-naLWiRfmtH4UJgtUktRTLw6FdoZJ2RvCR9ePbwM9aRMsS/KjFerkPZG9epEvXRAw5d5oPdrs9+3p+afNjxW8Xw==",
"dev": true,
"requires": {
"acorn": "^5.7.3",
@@ -21846,9 +21859,9 @@
"dev": true
},
"commander": {
- "version": "2.18.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz",
- "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==",
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
+ "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
"dev": true
}
}
@@ -22052,11 +22065,12 @@
}
},
"webpack-livereload-plugin": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/webpack-livereload-plugin/-/webpack-livereload-plugin-2.1.1.tgz",
- "integrity": "sha512-W7Q55QbPvVJotpIZSjjwzmqQ22333ExYxWM3WFlHKkbPStQqVRSmJkjntUqXF9jtpdeXi8r8HLkA1RVnAP0SQA==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/webpack-livereload-plugin/-/webpack-livereload-plugin-2.2.0.tgz",
+ "integrity": "sha512-sx9xA5mHoNOUgLQI0PmXT3KV9ecsVmUaTgr+fsoL69qAOHw/7VzkL1+ZMDQ8n0dPbWounswK6cBRSgMod7Nhgg==",
"dev": true,
"requires": {
+ "portfinder": "^1.0.17",
"tiny-lr": "^1.1.1"
}
},
diff --git a/package.json b/package.json
index ea6cd663dbca0..2299994e4f385 100644
--- a/package.json
+++ b/package.json
@@ -75,7 +75,6 @@
"@wordpress/npm-package-json-lint-config": "file:packages/npm-package-json-lint-config",
"@wordpress/postcss-themes": "file:packages/postcss-themes",
"@wordpress/scripts": "file:packages/scripts",
- "babel-loader": "8.0.5",
"benchmark": "2.1.4",
"browserslist": "4.4.1",
"chalk": "2.4.1",
@@ -104,6 +103,7 @@
"node-watch": "0.6.0",
"pegjs": "0.10.0",
"phpegjs": "1.0.0-beta7",
+ "postcss": "7.0.13",
"react-dom": "16.6.3",
"react-test-renderer": "16.6.3",
"redux": "4.0.0",
@@ -113,13 +113,10 @@
"shallow-equal": "1.0.0",
"shallow-equals": "1.0.0",
"shallowequal": "1.1.0",
- "source-map-loader": "0.2.3",
"sprintf-js": "1.1.1",
"stylelint-config-wordpress": "13.1.0",
"uuid": "3.3.2",
"webpack": "4.8.3",
- "webpack-bundle-analyzer": "3.0.2",
- "webpack-livereload-plugin": "2.1.1",
"webpack-rtl-plugin": "github:yoavf/webpack-rtl-plugin#develop"
},
"npmPackageJsonLintConfig": {
diff --git a/packages/block-serialization-default-parser/test/index.js b/packages/block-serialization-default-parser/test/index.js
index a3c67e280ef94..72f4121ce1944 100644
--- a/packages/block-serialization-default-parser/test/index.js
+++ b/packages/block-serialization-default-parser/test/index.js
@@ -6,7 +6,8 @@ import path from 'path';
/**
* WordPress dependencies
*/
-import { jsTester, phpTester } from '@wordpress/block-serialization-spec-parser';
+// eslint-disable-next-line no-restricted-syntax
+import { jsTester, phpTester } from '@wordpress/block-serialization-spec-parser/shared-tests';
/**
* Internal dependencies
diff --git a/packages/block-serialization-spec-parser/CHANGELOG.md b/packages/block-serialization-spec-parser/CHANGELOG.md
index e4f403ffd6849..7ba44f0cd9a96 100644
--- a/packages/block-serialization-spec-parser/CHANGELOG.md
+++ b/packages/block-serialization-spec-parser/CHANGELOG.md
@@ -1,6 +1,12 @@
-## 2.1.0 (Unreleased)
+## 3.0.0 (Unreleased)
-- A `parser.php` file generated from the PEGJS grammar is now included.
+## Breaking Change
+
+- A `parser.js` file generated from the PEGJS grammar is now outputted in commonjs format.
+
+## New Feature
+
+- A `parser.php` file generated from the PEGJS grammar is now added upon installation.
## 2.0.2 (2018-12-12)
diff --git a/packages/block-serialization-spec-parser/index.js b/packages/block-serialization-spec-parser/index.js
deleted file mode 100644
index b217f8f5c5ad7..0000000000000
--- a/packages/block-serialization-spec-parser/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export { parse } from './parser';
-export { jsTester, phpTester } from './shared-tests';
diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json
index 918c269169f09..5d40a8cfd03de 100644
--- a/packages/block-serialization-spec-parser/package.json
+++ b/packages/block-serialization-spec-parser/package.json
@@ -18,12 +18,16 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "main": "parser.js",
+ "dependencies": {
+ "pegjs": "^0.10.0"
+ },
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "concurrently \"npm run build:js\" \"npm run build:php\"",
- "build:js": "pegjs --format umd -o ./parser.js ./grammar.pegjs",
+ "build:js": "pegjs --format commonjs -o ./parser.js ./grammar.pegjs",
"build:php": "node bin/create-php-parser.js"
}
}
diff --git a/packages/block-serialization-spec-parser/test/index.js b/packages/block-serialization-spec-parser/test/index.js
index 7bdbe9f053f16..9d00c5a5434c3 100644
--- a/packages/block-serialization-spec-parser/test/index.js
+++ b/packages/block-serialization-spec-parser/test/index.js
@@ -6,7 +6,8 @@ import path from 'path';
/**
* Internal dependencies
*/
-import { jsTester, phpTester, parse } from '../';
+import { parse } from '../';
+import { jsTester, phpTester } from '../shared-tests';
describe( 'block-serialization-spec-parser-js', jsTester( parse ) );
diff --git a/packages/scripts/config/webpack.config.js b/packages/scripts/config/webpack.config.js
new file mode 100644
index 0000000000000..44d0ff61d9a65
--- /dev/null
+++ b/packages/scripts/config/webpack.config.js
@@ -0,0 +1,109 @@
+/**
+ * External dependencies
+ */
+const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' );
+const LiveReloadPlugin = require( 'webpack-livereload-plugin' );
+const path = require( 'path' );
+
+/**
+ * Internal dependencies
+ */
+const { camelCaseDash } = require( '../utils' );
+
+/**
+ * Converts @wordpress/* string request into request object.
+ *
+ * Note this isn't the same as camel case because of the
+ * way that numbers don't trigger the capitalized next letter.
+ *
+ * @example
+ * formatRequest( '@wordpress/api-fetch' );
+ * // { this: [ 'wp', 'apiFetch' ] }
+ * formatRequest( '@wordpress/i18n' );
+ * // { this: [ 'wp', 'i18n' ] }
+ *
+ * @param {string} request Request name from import statement.
+ * @return {Object} Request object formatted for further processing.
+ */
+const formatRequest = ( request ) => {
+ // '@wordpress/api-fetch' -> [ '@wordpress', 'api-fetch' ]
+ const [ , name ] = request.split( '/' );
+
+ // { this: [ 'wp', 'apiFetch' ] }
+ return {
+ this: [ 'wp', camelCaseDash( name ) ],
+ };
+};
+
+const wordpressExternals = ( context, request, callback ) => {
+ if ( /^@wordpress\//.test( request ) ) {
+ callback( null, formatRequest( request ), 'this' );
+ } else {
+ callback();
+ }
+};
+
+const externals = [
+ {
+ react: 'React',
+ 'react-dom': 'ReactDOM',
+ moment: 'moment',
+ jquery: 'jQuery',
+ lodash: 'lodash',
+ 'lodash-es': 'lodash',
+ },
+ wordpressExternals,
+];
+
+const isProduction = process.env.NODE_ENV === 'production';
+const mode = isProduction ? 'production' : 'development';
+
+const config = {
+ mode,
+ entry: {
+ index: path.resolve( process.cwd(), 'src', 'index.js' ),
+ },
+ output: {
+ filename: '[name].js',
+ path: path.resolve( process.cwd(), 'build' ),
+ },
+ externals,
+ resolve: {
+ alias: {
+ 'lodash-es': 'lodash',
+ },
+ },
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ use: require.resolve( 'source-map-loader' ),
+ enforce: 'pre',
+ },
+ {
+ test: /\.js$/,
+ exclude: /node_modules/,
+ use: require.resolve( 'babel-loader' ),
+ },
+ ],
+ },
+ plugins: [
+ // WP_BUNDLE_ANALYZER global variable enables utility that represents bundle content
+ // as convenient interactive zoomable treemap.
+ process.env.WP_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(),
+ // WP_LIVE_RELOAD_PORT global variable changes port on which live reload works
+ // when running watch mode.
+ ! isProduction && new LiveReloadPlugin( { port: process.env.WP_LIVE_RELOAD_PORT || 35729 } ),
+ ].filter( Boolean ),
+ stats: {
+ children: false,
+ },
+};
+
+if ( ! isProduction ) {
+ // WP_DEVTOOL global variable controls how source maps are generated.
+ // See: https://webpack.js.org/configuration/devtool/#devtool.
+ config.devtool = process.env.WP_DEVTOOL || 'source-map';
+}
+
+module.exports = config;
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 8aaf6e8ee7ac6..388bc0a26a3b8 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -35,6 +35,7 @@
"@wordpress/eslint-plugin": "file:../eslint-plugin",
"@wordpress/jest-preset-default": "file:../jest-preset-default",
"@wordpress/npm-package-json-lint-config": "file:../npm-package-json-lint-config",
+ "babel-loader": "^8.0.5",
"chalk": "^2.4.1",
"check-node-version": "^3.1.1",
"cross-spawn": "^5.1.0",
@@ -45,10 +46,13 @@
"puppeteer": "1.6.1",
"read-pkg-up": "^1.0.1",
"resolve-bin": "^0.4.0",
+ "source-map-loader": "^0.2.4",
"stylelint": "^9.10.1",
"stylelint-config-wordpress": "^13.1.0",
"webpack": "4.8.3",
- "webpack-cli": "^3.1.2"
+ "webpack-bundle-analyzer": "^3.0.3",
+ "webpack-cli": "^3.1.2",
+ "webpack-livereload-plugin": "^2.2.0"
},
"publishConfig": {
"access": "public"
diff --git a/packages/scripts/scripts/build.js b/packages/scripts/scripts/build.js
index 9470274f76633..ea2c7d303f2fa 100644
--- a/packages/scripts/scripts/build.js
+++ b/packages/scripts/scripts/build.js
@@ -32,4 +32,3 @@ if ( hasWebpackConfig ) {
console.log( 'Webpack config file is missing.' );
process.exit( 1 );
}
-
diff --git a/packages/scripts/utils/index.js b/packages/scripts/utils/index.js
index 473952fcdfc17..b1a78d0d600c9 100644
--- a/packages/scripts/utils/index.js
+++ b/packages/scripts/utils/index.js
@@ -18,8 +18,12 @@ const {
const {
hasPackageProp,
} = require( './package' );
+const {
+ camelCaseDash,
+} = require( './string' );
module.exports = {
+ camelCaseDash,
fromConfigRoot,
getCliArg,
getCliArgs,
diff --git a/packages/scripts/utils/string.js b/packages/scripts/utils/string.js
new file mode 100644
index 0000000000000..ea6ea3889c1ad
--- /dev/null
+++ b/packages/scripts/utils/string.js
@@ -0,0 +1,20 @@
+/**
+ * Given a string, returns a new string with dash separators converted to
+ * camelCase equivalent. This is not as aggressive as `_.camelCase` in
+ * converting to uppercase, where Lodash will also capitalize letters
+ * following numbers.
+ *
+ * @param {string} string Input dash-delimited string.
+ *
+ * @return {string} Camel-cased string.
+ */
+function camelCaseDash( string ) {
+ return string.replace(
+ /-([a-z])/g,
+ ( match, letter ) => letter.toUpperCase()
+ );
+}
+
+module.exports = {
+ camelCaseDash,
+};
diff --git a/packages/scripts/utils/test/string.js b/packages/scripts/utils/test/string.js
new file mode 100644
index 0000000000000..ca81bdda5df54
--- /dev/null
+++ b/packages/scripts/utils/test/string.js
@@ -0,0 +1,23 @@
+/**
+ * Internal dependencies
+ */
+import { camelCaseDash } from '../string';
+
+describe( 'string', () => {
+ describe( 'camelCaseDash', () => {
+ test( 'does not change a single word', () => {
+ expect( camelCaseDash( 'blocks' ) ).toBe( 'blocks' );
+ expect( camelCaseDash( 'dom' ) ).toBe( 'dom' );
+ } );
+
+ test( 'does not capitalize letters following numbers', () => {
+ expect( camelCaseDash( 'a11y' ) ).toBe( 'a11y' );
+ expect( camelCaseDash( 'i18n' ) ).toBe( 'i18n' );
+ } );
+
+ test( 'converts dashes into camel case', () => {
+ expect( camelCaseDash( 'api-fetch' ) ).toBe( 'apiFetch' );
+ expect( camelCaseDash( 'list-reusable-blocks' ) ).toBe( 'listReusableBlocks' );
+ } );
+ } );
+} );
diff --git a/webpack.config.js b/webpack.config.js
index c67b8c95c804d..a5dd63c3bd982 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,10 +3,8 @@
*/
const { DefinePlugin } = require( 'webpack' );
const WebpackRTLPlugin = require( 'webpack-rtl-plugin' );
-const LiveReloadPlugin = require( 'webpack-livereload-plugin' );
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
const postcss = require( 'postcss' );
-
const { get } = require( 'lodash' );
const { basename } = require( 'path' );
@@ -15,7 +13,8 @@ const { basename } = require( 'path' );
*/
const CustomTemplatedPathPlugin = require( '@wordpress/custom-templated-path-webpack-plugin' );
const LibraryExportDefaultPlugin = require( '@wordpress/library-export-default-webpack-plugin' );
-const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' );
+const config = require( '@wordpress/scripts/config/webpack.config' );
+const { camelCaseDash } = require( '@wordpress/scripts/utils' );
/**
* Internal dependencies
@@ -24,165 +23,86 @@ const { dependencies } = require( './package' );
const WORDPRESS_NAMESPACE = '@wordpress/';
-/**
- * Given a string, returns a new string with dash separators converted to
- * camelCase equivalent. This is not as aggressive as `_.camelCase` in
- * converting to uppercase, where Lodash will also capitalize letters
- * following numbers.
- *
- * @param {string} string Input dash-delimited string.
- *
- * @return {string} Camel-cased string.
- */
-function camelCaseDash( string ) {
- return string.replace(
- /-([a-z])/g,
- ( match, letter ) => letter.toUpperCase()
- );
-}
-
const gutenbergPackages = Object.keys( dependencies )
.filter( ( packageName ) => packageName.startsWith( WORDPRESS_NAMESPACE ) )
.map( ( packageName ) => packageName.replace( WORDPRESS_NAMESPACE, '' ) );
-const externals = {
- react: 'React',
- 'react-dom': 'ReactDOM',
- moment: 'moment',
- jquery: 'jQuery',
- lodash: 'lodash',
- 'lodash-es': 'lodash',
+config.entry = gutenbergPackages.reduce( ( memo, packageName ) => {
+ const name = camelCaseDash( packageName );
+ memo[ name ] = `./packages/${ packageName }`;
+ return memo;
+}, {} );
+
+config.output = {
+ filename: './build/[basename]/index.js',
+ path: __dirname,
+ library: [ 'wp', '[name]' ],
+ libraryTarget: 'this',
};
-gutenbergPackages.forEach( ( name ) => {
- externals[ WORDPRESS_NAMESPACE + name ] = {
- this: [ 'wp', camelCaseDash( name ) ],
- };
-} );
-
-const isProduction = process.env.NODE_ENV === 'production';
-const mode = isProduction ? 'production' : 'development';
-
-const config = {
- mode,
- entry: gutenbergPackages.reduce( ( memo, packageName ) => {
- const name = camelCaseDash( packageName );
- memo[ name ] = `./packages/${ packageName }`;
- return memo;
- }, {} ),
- output: {
- filename: './build/[basename]/index.js',
- path: __dirname,
- library: [ 'wp', '[name]' ],
- libraryTarget: 'this',
- },
- externals,
- resolve: {
- modules: [
- __dirname,
- 'node_modules',
- ],
- alias: {
- 'lodash-es': 'lodash',
+config.plugins.push(
+ new DefinePlugin( {
+ // Inject the `GUTENBERG_PHASE` global, used for feature flagging.
+ // eslint-disable-next-line @wordpress/gutenberg-phase
+ 'process.env.GUTENBERG_PHASE': JSON.stringify( parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ) || 1 ),
+ } ),
+ // Create RTL files with a -rtl suffix
+ new WebpackRTLPlugin( {
+ suffix: '-rtl',
+ minify: config.mode === 'production' ? { safe: true } : false,
+ } ),
+ new CustomTemplatedPathPlugin( {
+ basename( path, data ) {
+ let rawRequest;
+
+ const entryModule = get( data, [ 'chunk', 'entryModule' ], {} );
+ switch ( entryModule.type ) {
+ case 'javascript/auto':
+ rawRequest = entryModule.rawRequest;
+ break;
+
+ case 'javascript/esm':
+ rawRequest = entryModule.rootModule.rawRequest;
+ break;
+ }
+
+ if ( rawRequest ) {
+ return basename( rawRequest );
+ }
+
+ return path;
},
- },
- module: {
- rules: [
- {
- test: /\.js$/,
- use: [ 'source-map-loader' ],
- enforce: 'pre',
- },
- {
- test: /\.js$/,
- exclude: [
- /block-serialization-spec-parser/,
- /is-shallow-equal/,
- /node_modules/,
- ],
- use: 'babel-loader',
- },
- ],
- },
- plugins: [
- new DefinePlugin( {
- // Inject the `GUTENBERG_PHASE` global, used for feature flagging.
- // eslint-disable-next-line @wordpress/gutenberg-phase
- 'process.env.GUTENBERG_PHASE': JSON.stringify( parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ) || 1 ),
- } ),
- // Create RTL files with a -rtl suffix
- new WebpackRTLPlugin( {
- suffix: '-rtl',
- minify: process.env.NODE_ENV === 'production' ? { safe: true } : false,
- } ),
- new CustomTemplatedPathPlugin( {
- basename( path, data ) {
- let rawRequest;
-
- const entryModule = get( data, [ 'chunk', 'entryModule' ], {} );
- switch ( entryModule.type ) {
- case 'javascript/auto':
- rawRequest = entryModule.rawRequest;
- break;
-
- case 'javascript/esm':
- rawRequest = entryModule.rootModule.rawRequest;
- break;
- }
-
- if ( rawRequest ) {
- return basename( rawRequest );
+ } ),
+ new LibraryExportDefaultPlugin( [
+ 'api-fetch',
+ 'deprecated',
+ 'dom-ready',
+ 'redux-routine',
+ 'token-list',
+ ].map( camelCaseDash ) ),
+ new CopyWebpackPlugin(
+ gutenbergPackages.map( ( packageName ) => ( {
+ from: `./packages/${ packageName }/build-style/*.css`,
+ to: `./build/${ packageName }/`,
+ flatten: true,
+ transform: ( content ) => {
+ if ( config.mode === 'production' ) {
+ return postcss( [
+ require( 'cssnano' )( {
+ preset: [ 'default', {
+ discardComments: {
+ removeAll: true,
+ },
+ } ],
+ } ),
+ ] )
+ .process( content, { from: 'src/app.css', to: 'dest/app.css' } )
+ .then( ( result ) => result.css );
}
-
- return path;
+ return content;
},
- } ),
- new LibraryExportDefaultPlugin( [
- 'api-fetch',
- 'deprecated',
- 'dom-ready',
- 'redux-routine',
- 'token-list',
- ].map( camelCaseDash ) ),
- new CopyWebpackPlugin(
- gutenbergPackages.map( ( packageName ) => ( {
- from: `./packages/${ packageName }/build-style/*.css`,
- to: `./build/${ packageName }/`,
- flatten: true,
- transform: ( content ) => {
- if ( config.mode === 'production' ) {
- return postcss( [
- require( 'cssnano' )( {
- preset: [ 'default', {
- discardComments: {
- removeAll: true,
- },
- } ],
- } ),
- ] )
- .process( content, { from: 'src/app.css', to: 'dest/app.css' } )
- .then( ( result ) => result.css );
- }
- return content;
- },
- } ) )
- ),
- // GUTENBERG_BUNDLE_ANALYZER global variable enables utility that represents bundle content
- // as convenient interactive zoomable treemap.
- process.env.GUTENBERG_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(),
- // GUTENBERG_LIVE_RELOAD_PORT global variable changes port on which live reload works
- // when running watch mode.
- ! isProduction && new LiveReloadPlugin( { port: process.env.GUTENBERG_LIVE_RELOAD_PORT || 35729 } ),
- ].filter( Boolean ),
- stats: {
- children: false,
- },
-};
-
-if ( ! isProduction ) {
- // GUTENBERG_DEVTOOL global variable controls how source maps are generated.
- // See: https://webpack.js.org/configuration/devtool/#devtool.
- config.devtool = process.env.GUTENBERG_DEVTOOL || 'source-map';
-}
+ } ) )
+ )
+);
module.exports = config;
From 011a8b3d92f2bdd8b79853bb53d292999dc4c5dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9s?=
Date: Thu, 21 Feb 2019 13:38:33 +0100
Subject: [PATCH 006/169] Use globals instead of imports in tutorials (#13995)
* Use globals instead of imports
* Update docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
Co-Authored-By: nosolosw
* Update docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
Co-Authored-By: nosolosw
---
.../developers/tutorials/metabox/meta-block-3-add.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
index 4f6e54f148ed7..e1e6749c17ff7 100644
--- a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
+++ b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
@@ -60,8 +60,9 @@ Add this code to your JavaScript file (this tutorial will call the file `myguten
```
{% ESNext %}
```jsx
-import { registerBlockType } from '@wordpress/blocks';
-import { TextControl } from '@wordpress/components';
+
+const { registerBlockType } = wp.blocks;
+const { TextControl } = wp.components;
registerBlockType( 'myguten/meta-block', {
title: 'Meta Block',
From 10989f01abbb187805443fa9e0e839a55ae07a63 Mon Sep 17 00:00:00 2001
From: Kjell Reigstad
Date: Thu, 21 Feb 2019 09:46:45 -0500
Subject: [PATCH 007/169] URL input popover visual cleanup (#13973)
* Use chevron instead of ellipsis in url input field options.
* Mimic toolbar icon styles for the icons in the URL popover.
* Add a left divider before the URL settings toggle
* Even up the spacing in the settings panel.
* Add periods to code comments.
* Update snapshot
---
.../src/components/url-popover/index.js | 2 +-
.../src/components/url-popover/style.scss | 46 ++++++++++++++++---
.../test/__snapshots__/index.js.snap | 4 +-
3 files changed, 43 insertions(+), 9 deletions(-)
diff --git a/packages/editor/src/components/url-popover/index.js b/packages/editor/src/components/url-popover/index.js
index c89f76d8bfd62..5842dd6017b5e 100644
--- a/packages/editor/src/components/url-popover/index.js
+++ b/packages/editor/src/components/url-popover/index.js
@@ -54,7 +54,7 @@ class URLPopover extends Component {
{ !! renderSettings && (
svg {
+ padding: 5px;
+ border-radius: $radius-round-rectangle;
+ height: 30px;
+ width: 30px;
+ }
+
+ &:not(:disabled):not([aria-disabled="true"]):not(.is-default):hover {
+ box-shadow: none;
+
+ > svg {
+ @include formatting-button-style__hover;
+ }
+ }
+
+ &:not(:disabled):focus {
+ box-shadow: none;
+
+ > svg {
+ @include formatting-button-style__focus;
+ }
+ }
+ }
+
&__settings-toggle {
flex-shrink: 0;
- width: $icon-button-size;
- height: $icon-button-size;
- .dashicon {
- transform: rotate(90deg);
+ // Add a left divider to the toggle button.
+ border-radius: 0;
+ border-left: $border-width solid $light-gray-500;
+ margin-left: 1px;
+
+ &[aria-expanded="true"] .dashicon {
+ transform: rotate(180deg);
}
}
&__settings {
- padding: 7px 8px;
+ padding: $panel-padding;
border-top: $border-width solid $light-gray-500;
- padding-top: 7px + $border-width;
+
+ .components-base-control:last-child .components-base-control__field {
+ margin-bottom: 0;
+ }
}
}
diff --git a/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap b/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap
index 4bffa2ecb3747..f8f0aa9027bdc 100644
--- a/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap
@@ -16,7 +16,7 @@ exports[`URLPopover matches the snapshot in its default state 1`] = `
@@ -40,7 +40,7 @@ exports[`URLPopover matches the snapshot when the settings are toggled open 1`]
From f5e15ae16fd96149146cb92e085ab75e5ccf4fee Mon Sep 17 00:00:00 2001
From: Stefanos Togoulidis
Date: Thu, 21 Feb 2019 16:51:23 +0200
Subject: [PATCH 008/169] Reinstate "underline" in default formats list
(#14008)
---
packages/format-library/src/default-formats.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/packages/format-library/src/default-formats.js b/packages/format-library/src/default-formats.js
index 46d8e4b851e4a..f5d57910a7f13 100644
--- a/packages/format-library/src/default-formats.js
+++ b/packages/format-library/src/default-formats.js
@@ -7,6 +7,7 @@ import { image } from './image';
import { italic } from './italic';
import { link } from './link';
import { strikethrough } from './strikethrough';
+import { underline } from './underline';
export default [
bold,
@@ -15,4 +16,5 @@ export default [
italic,
link,
strikethrough,
+ underline,
];
From 6f88bec8fbf15291e3e00872a5df59e310494f5b Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Thu, 21 Feb 2019 16:35:39 +0100
Subject: [PATCH 009/169] Upgrade React to 16.8.2: Welcome React Hooks (#13992)
* Upgrade React to 16.8.2
* Update package-lock.json file
* Expose React Hooks
---
lib/client-assets.php | 4 +-
package-lock.json | 200 +++---------------
package.json | 4 +-
packages/element/package.json | 4 +-
packages/element/src/react.js | 26 +++
phpunit/class-vendor-script-filename-test.php | 8 +-
6 files changed, 68 insertions(+), 178 deletions(-)
diff --git a/lib/client-assets.php b/lib/client-assets.php
index b34e428425e80..e1a491fa9ef4e 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -540,12 +540,12 @@ function gutenberg_register_vendor_scripts() {
gutenberg_register_vendor_script(
'react',
- 'https://unpkg.com/react@16.6.3/umd/react' . $react_suffix . '.js',
+ 'https://unpkg.com/react@16.8.2/umd/react' . $react_suffix . '.js',
array( 'wp-polyfill' )
);
gutenberg_register_vendor_script(
'react-dom',
- 'https://unpkg.com/react-dom@16.6.3/umd/react-dom' . $react_suffix . '.js',
+ 'https://unpkg.com/react-dom@16.8.2/umd/react-dom' . $react_suffix . '.js',
array( 'react' )
);
$moment_script = SCRIPT_DEBUG ? 'moment.js' : 'min/moment.min.js';
diff --git a/package-lock.json b/package-lock.json
index 656ba9ae3cd0e..92c991e0c93f8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2803,8 +2803,8 @@
"@babel/runtime": "^7.3.1",
"@wordpress/escape-html": "file:packages/escape-html",
"lodash": "^4.17.11",
- "react": "^16.6.3",
- "react-dom": "^16.6.3"
+ "react": "^16.8.2",
+ "react-dom": "^16.8.2"
}
},
"@wordpress/escape-html": {
@@ -3164,17 +3164,6 @@
"object.entries": "^1.0.4",
"prop-types": "^15.6.1",
"prop-types-exact": "^1.1.2"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
}
},
"ajv": {
@@ -7452,24 +7441,6 @@
"prop-types": "^15.6.2",
"react-is": "^16.5.2",
"react-test-renderer": "^16.0.0-0"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- },
- "react-is": {
- "version": "16.6.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.0.tgz",
- "integrity": "sha512-q8U7k0Fi7oxF1HvQgyBjPwDXeMplEsArnKt2iYhuIF86+GBbgLHdAmokL3XUFjTd7Q363OSNG55FOGUdONVn1g==",
- "dev": true
- }
}
},
"enzyme-adapter-utils": {
@@ -7481,18 +7452,6 @@
"function.prototype.name": "^1.1.0",
"object.assign": "^4.1.0",
"prop-types": "^15.6.2"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
}
},
"enzyme-to-json": {
@@ -7816,18 +7775,6 @@
"has": "^1.0.1",
"jsx-ast-utils": "^2.0.1",
"prop-types": "^15.6.0"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
}
},
"eslint-scope": {
@@ -10244,6 +10191,11 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ },
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -17785,12 +17737,13 @@
}
},
"prop-types": {
- "version": "15.5.10",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz",
- "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=",
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
- "fbjs": "^0.8.9",
- "loose-envify": "^1.3.1"
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
}
},
"prop-types-exact": {
@@ -18055,25 +18008,14 @@
"integrity": "sha512-pLJkPbZCe+3ml+9Q15z+R69qYZDsluj0KwrdFb8kSNaqDzYAveDUblf7voHH9hNTdKIiIvP8iIdGFFKSgffVaQ=="
},
"react": {
- "version": "16.6.3",
- "resolved": "https://registry.npmjs.org/react/-/react-16.6.3.tgz",
- "integrity": "sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==",
+ "version": "16.8.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.8.2.tgz",
+ "integrity": "sha512-aB2ctx9uQ9vo09HVknqv3DGRpI7OIGJhCx3Bt0QqoRluEjHSaObJl+nG12GDdYH6sTgE7YiPJ6ZUyMx9kICdXw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
- "scheduler": "^0.11.2"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
+ "scheduler": "^0.13.2"
}
},
"react-addons-shallow-compare": {
@@ -18128,46 +18070,23 @@
"react-portal": "^4.1.5",
"react-with-styles": "^3.2.0",
"react-with-styles-interface-css": "^4.0.2"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
}
},
"react-dom": {
- "version": "16.6.3",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.3.tgz",
- "integrity": "sha512-8ugJWRCWLGXy+7PmNh8WJz3g1TaTUt1XyoIcFN+x0Zbkoz+KKdUyx1AQLYJdbFXjuF41Nmjn5+j//rxvhFjgSQ==",
+ "version": "16.8.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.2.tgz",
+ "integrity": "sha512-cPGfgFfwi+VCZjk73buu14pYkYBR1b/SRMSYqkLDdhSEHnSwcuYTPu6/Bh6ZphJFIk80XLvbSe2azfcRzNF+Xg==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
- "scheduler": "^0.11.2"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
+ "scheduler": "^0.13.2"
}
},
"react-is": {
- "version": "16.6.3",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz",
- "integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==",
- "dev": true
+ "version": "16.8.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz",
+ "integrity": "sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q=="
},
"react-moment-proptypes": {
"version": "1.6.0",
@@ -18186,17 +18105,6 @@
"consolidated-events": "^1.1.1 || ^2.0.0",
"object.values": "^1.0.4",
"prop-types": "^15.6.1"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
}
},
"react-portal": {
@@ -18208,27 +18116,15 @@
}
},
"react-test-renderer": {
- "version": "16.6.3",
- "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.6.3.tgz",
- "integrity": "sha512-B5bCer+qymrQz/wN03lT0LppbZUDRq6AMfzMKrovzkGzfO81a9T+PWQW6MzkWknbwODQH/qpJno/yFQLX5IWrQ==",
+ "version": "16.8.2",
+ "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.2.tgz",
+ "integrity": "sha512-gsd4NoOaYrZD2R8zi+CBV9wTGMsGhE2bRe4wvenGy0WcLJgdPscRZDDz+kmLjY+/5XpYC8yRR/v4CScgYfGyoQ==",
"dev": true,
"requires": {
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
- "react-is": "^16.6.3",
- "scheduler": "^0.11.2"
- },
- "dependencies": {
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
+ "react-is": "^16.8.2",
+ "scheduler": "^0.13.2"
}
},
"react-with-direction": {
@@ -18244,22 +18140,6 @@
"object.assign": "^4.1.0",
"object.values": "^1.0.4",
"prop-types": "^15.6.0"
- },
- "dependencies": {
- "hoist-non-react-statics": {
- "version": "2.5.5",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
- "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
- },
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
}
},
"react-with-styles": {
@@ -18271,22 +18151,6 @@
"hoist-non-react-statics": "^2.5.0",
"prop-types": "^15.6.1",
"react-with-direction": "^1.3.0"
- },
- "dependencies": {
- "hoist-non-react-statics": {
- "version": "2.5.5",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
- "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
- },
- "prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
- "requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
- }
- }
}
},
"react-with-styles-interface-css": {
@@ -19337,9 +19201,9 @@
"dev": true
},
"scheduler": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.2.tgz",
- "integrity": "sha512-+WCP3s3wOaW4S7C1tl3TEXp4l9lJn0ZK8G3W3WKRWmw77Z2cIFUW2MiNTMHn5sCjxN+t7N43HAOOgMjyAg5hlg==",
+ "version": "0.13.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.2.tgz",
+ "integrity": "sha512-qK5P8tHS7vdEMCW5IPyt8v9MJOHqTrOUgPXib7tqm9vh834ibBX5BNhwkplX/0iOzHW5sXyluehYfS9yrkz9+w==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
diff --git a/package.json b/package.json
index 2299994e4f385..59316766071af 100644
--- a/package.json
+++ b/package.json
@@ -104,8 +104,8 @@
"pegjs": "0.10.0",
"phpegjs": "1.0.0-beta7",
"postcss": "7.0.13",
- "react-dom": "16.6.3",
- "react-test-renderer": "16.6.3",
+ "react-dom": "16.8.2",
+ "react-test-renderer": "16.8.2",
"redux": "4.0.0",
"rimraf": "2.6.2",
"rtlcss": "2.4.0",
diff --git a/packages/element/package.json b/packages/element/package.json
index 24df06e6702af..0aa0e7141202f 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -24,8 +24,8 @@
"@babel/runtime": "^7.3.1",
"@wordpress/escape-html": "file:../escape-html",
"lodash": "^4.17.11",
- "react": "^16.6.3",
- "react-dom": "^16.6.3"
+ "react": "^16.8.2",
+ "react-dom": "^16.8.2"
},
"publishConfig": {
"access": "public"
diff --git a/packages/element/src/react.js b/packages/element/src/react.js
index 4d42fae21c1f7..bb8b98d79d1d5 100644
--- a/packages/element/src/react.js
+++ b/packages/element/src/react.js
@@ -12,6 +12,16 @@ import {
Fragment,
isValidElement,
StrictMode,
+ useState,
+ useEffect,
+ useContext,
+ useReducer,
+ useCallback,
+ useMemo,
+ useRef,
+ useImperativeHandle,
+ useLayoutEffect,
+ useDebugValue,
} from 'react';
import { isString } from 'lodash';
@@ -93,6 +103,22 @@ export { isValidElement };
export { StrictMode };
+/**
+ * Make React Hooks available
+ */
+export {
+ useCallback,
+ useContext,
+ useDebugValue,
+ useEffect,
+ useImperativeHandle,
+ useLayoutEffect,
+ useMemo,
+ useReducer,
+ useRef,
+ useState,
+};
+
/**
* Concatenate two or more React children objects.
*
diff --git a/phpunit/class-vendor-script-filename-test.php b/phpunit/class-vendor-script-filename-test.php
index 0ee7a7b78b83d..ef9917e476f86 100644
--- a/phpunit/class-vendor-script-filename-test.php
+++ b/phpunit/class-vendor-script-filename-test.php
@@ -11,23 +11,23 @@ function vendor_script_filename_cases() {
// Development mode scripts.
array(
'react-handle',
- 'https://unpkg.com/react@16.6.3/umd/react.development.js',
+ 'https://unpkg.com/react@16.8.2/umd/react.development.js',
'react-handle.HASH.js',
),
array(
'react-dom-handle',
- 'https://unpkg.com/react-dom@16.6.3/umd/react-dom.development.js',
+ 'https://unpkg.com/react-dom@16.8.2/umd/react-dom.development.js',
'react-dom-handle.HASH.js',
),
// Production mode scripts.
array(
'react-handle',
- 'https://unpkg.com/react@16.6.3/umd/react.production.min.js',
+ 'https://unpkg.com/react@16.8.2/umd/react.production.min.js',
'react-handle.min.HASH.js',
),
array(
'react-dom-handle',
- 'https://unpkg.com/react-dom@16.6.3/umd/react-dom.production.min.js',
+ 'https://unpkg.com/react-dom@16.8.2/umd/react-dom.production.min.js',
'react-dom-handle.min.HASH.js',
),
// Other cases.
From bc065626fe83efee65a876364a6da51619042bed Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Thu, 21 Feb 2019 17:00:01 +0100
Subject: [PATCH 010/169] Revert "Upgrade React to 16.8.2: Welcome React Hooks
(#13992)" (#14017)
This reverts commit 6f88bec8fbf15291e3e00872a5df59e310494f5b.
---
lib/client-assets.php | 4 +-
package-lock.json | 200 +++++++++++++++---
package.json | 4 +-
packages/element/package.json | 4 +-
packages/element/src/react.js | 26 ---
phpunit/class-vendor-script-filename-test.php | 8 +-
6 files changed, 178 insertions(+), 68 deletions(-)
diff --git a/lib/client-assets.php b/lib/client-assets.php
index e1a491fa9ef4e..b34e428425e80 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -540,12 +540,12 @@ function gutenberg_register_vendor_scripts() {
gutenberg_register_vendor_script(
'react',
- 'https://unpkg.com/react@16.8.2/umd/react' . $react_suffix . '.js',
+ 'https://unpkg.com/react@16.6.3/umd/react' . $react_suffix . '.js',
array( 'wp-polyfill' )
);
gutenberg_register_vendor_script(
'react-dom',
- 'https://unpkg.com/react-dom@16.8.2/umd/react-dom' . $react_suffix . '.js',
+ 'https://unpkg.com/react-dom@16.6.3/umd/react-dom' . $react_suffix . '.js',
array( 'react' )
);
$moment_script = SCRIPT_DEBUG ? 'moment.js' : 'min/moment.min.js';
diff --git a/package-lock.json b/package-lock.json
index 92c991e0c93f8..656ba9ae3cd0e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2803,8 +2803,8 @@
"@babel/runtime": "^7.3.1",
"@wordpress/escape-html": "file:packages/escape-html",
"lodash": "^4.17.11",
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.6.3",
+ "react-dom": "^16.6.3"
}
},
"@wordpress/escape-html": {
@@ -3164,6 +3164,17 @@
"object.entries": "^1.0.4",
"prop-types": "^15.6.1",
"prop-types-exact": "^1.1.2"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"ajv": {
@@ -7441,6 +7452,24 @@
"prop-types": "^15.6.2",
"react-is": "^16.5.2",
"react-test-renderer": "^16.0.0-0"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "react-is": {
+ "version": "16.6.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.0.tgz",
+ "integrity": "sha512-q8U7k0Fi7oxF1HvQgyBjPwDXeMplEsArnKt2iYhuIF86+GBbgLHdAmokL3XUFjTd7Q363OSNG55FOGUdONVn1g==",
+ "dev": true
+ }
}
},
"enzyme-adapter-utils": {
@@ -7452,6 +7481,18 @@
"function.prototype.name": "^1.1.0",
"object.assign": "^4.1.0",
"prop-types": "^15.6.2"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"enzyme-to-json": {
@@ -7775,6 +7816,18 @@
"has": "^1.0.1",
"jsx-ast-utils": "^2.0.1",
"prop-types": "^15.6.0"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"eslint-scope": {
@@ -10191,11 +10244,6 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
- "hoist-non-react-statics": {
- "version": "2.5.5",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
- "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
- },
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -17737,13 +17785,12 @@
}
},
"prop-types": {
- "version": "15.7.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
- "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "version": "15.5.10",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz",
+ "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=",
"requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.8.1"
+ "fbjs": "^0.8.9",
+ "loose-envify": "^1.3.1"
}
},
"prop-types-exact": {
@@ -18008,14 +18055,25 @@
"integrity": "sha512-pLJkPbZCe+3ml+9Q15z+R69qYZDsluj0KwrdFb8kSNaqDzYAveDUblf7voHH9hNTdKIiIvP8iIdGFFKSgffVaQ=="
},
"react": {
- "version": "16.8.2",
- "resolved": "https://registry.npmjs.org/react/-/react-16.8.2.tgz",
- "integrity": "sha512-aB2ctx9uQ9vo09HVknqv3DGRpI7OIGJhCx3Bt0QqoRluEjHSaObJl+nG12GDdYH6sTgE7YiPJ6ZUyMx9kICdXw==",
+ "version": "16.6.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.6.3.tgz",
+ "integrity": "sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
- "scheduler": "^0.13.2"
+ "scheduler": "^0.11.2"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"react-addons-shallow-compare": {
@@ -18070,23 +18128,46 @@
"react-portal": "^4.1.5",
"react-with-styles": "^3.2.0",
"react-with-styles-interface-css": "^4.0.2"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"react-dom": {
- "version": "16.8.2",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.2.tgz",
- "integrity": "sha512-cPGfgFfwi+VCZjk73buu14pYkYBR1b/SRMSYqkLDdhSEHnSwcuYTPu6/Bh6ZphJFIk80XLvbSe2azfcRzNF+Xg==",
+ "version": "16.6.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.3.tgz",
+ "integrity": "sha512-8ugJWRCWLGXy+7PmNh8WJz3g1TaTUt1XyoIcFN+x0Zbkoz+KKdUyx1AQLYJdbFXjuF41Nmjn5+j//rxvhFjgSQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
- "scheduler": "^0.13.2"
+ "scheduler": "^0.11.2"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"react-is": {
- "version": "16.8.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz",
- "integrity": "sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q=="
+ "version": "16.6.3",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz",
+ "integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==",
+ "dev": true
},
"react-moment-proptypes": {
"version": "1.6.0",
@@ -18105,6 +18186,17 @@
"consolidated-events": "^1.1.1 || ^2.0.0",
"object.values": "^1.0.4",
"prop-types": "^15.6.1"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"react-portal": {
@@ -18116,15 +18208,27 @@
}
},
"react-test-renderer": {
- "version": "16.8.2",
- "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.2.tgz",
- "integrity": "sha512-gsd4NoOaYrZD2R8zi+CBV9wTGMsGhE2bRe4wvenGy0WcLJgdPscRZDDz+kmLjY+/5XpYC8yRR/v4CScgYfGyoQ==",
+ "version": "16.6.3",
+ "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.6.3.tgz",
+ "integrity": "sha512-B5bCer+qymrQz/wN03lT0LppbZUDRq6AMfzMKrovzkGzfO81a9T+PWQW6MzkWknbwODQH/qpJno/yFQLX5IWrQ==",
"dev": true,
"requires": {
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
- "react-is": "^16.8.2",
- "scheduler": "^0.13.2"
+ "react-is": "^16.6.3",
+ "scheduler": "^0.11.2"
+ },
+ "dependencies": {
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"react-with-direction": {
@@ -18140,6 +18244,22 @@
"object.assign": "^4.1.0",
"object.values": "^1.0.4",
"prop-types": "^15.6.0"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ },
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"react-with-styles": {
@@ -18151,6 +18271,22 @@
"hoist-non-react-statics": "^2.5.0",
"prop-types": "^15.6.1",
"react-with-direction": "^1.3.0"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ },
+ "prop-types": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
+ "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ }
}
},
"react-with-styles-interface-css": {
@@ -19201,9 +19337,9 @@
"dev": true
},
"scheduler": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.2.tgz",
- "integrity": "sha512-qK5P8tHS7vdEMCW5IPyt8v9MJOHqTrOUgPXib7tqm9vh834ibBX5BNhwkplX/0iOzHW5sXyluehYfS9yrkz9+w==",
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.2.tgz",
+ "integrity": "sha512-+WCP3s3wOaW4S7C1tl3TEXp4l9lJn0ZK8G3W3WKRWmw77Z2cIFUW2MiNTMHn5sCjxN+t7N43HAOOgMjyAg5hlg==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
diff --git a/package.json b/package.json
index 59316766071af..2299994e4f385 100644
--- a/package.json
+++ b/package.json
@@ -104,8 +104,8 @@
"pegjs": "0.10.0",
"phpegjs": "1.0.0-beta7",
"postcss": "7.0.13",
- "react-dom": "16.8.2",
- "react-test-renderer": "16.8.2",
+ "react-dom": "16.6.3",
+ "react-test-renderer": "16.6.3",
"redux": "4.0.0",
"rimraf": "2.6.2",
"rtlcss": "2.4.0",
diff --git a/packages/element/package.json b/packages/element/package.json
index 0aa0e7141202f..24df06e6702af 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -24,8 +24,8 @@
"@babel/runtime": "^7.3.1",
"@wordpress/escape-html": "file:../escape-html",
"lodash": "^4.17.11",
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.6.3",
+ "react-dom": "^16.6.3"
},
"publishConfig": {
"access": "public"
diff --git a/packages/element/src/react.js b/packages/element/src/react.js
index bb8b98d79d1d5..4d42fae21c1f7 100644
--- a/packages/element/src/react.js
+++ b/packages/element/src/react.js
@@ -12,16 +12,6 @@ import {
Fragment,
isValidElement,
StrictMode,
- useState,
- useEffect,
- useContext,
- useReducer,
- useCallback,
- useMemo,
- useRef,
- useImperativeHandle,
- useLayoutEffect,
- useDebugValue,
} from 'react';
import { isString } from 'lodash';
@@ -103,22 +93,6 @@ export { isValidElement };
export { StrictMode };
-/**
- * Make React Hooks available
- */
-export {
- useCallback,
- useContext,
- useDebugValue,
- useEffect,
- useImperativeHandle,
- useLayoutEffect,
- useMemo,
- useReducer,
- useRef,
- useState,
-};
-
/**
* Concatenate two or more React children objects.
*
diff --git a/phpunit/class-vendor-script-filename-test.php b/phpunit/class-vendor-script-filename-test.php
index ef9917e476f86..0ee7a7b78b83d 100644
--- a/phpunit/class-vendor-script-filename-test.php
+++ b/phpunit/class-vendor-script-filename-test.php
@@ -11,23 +11,23 @@ function vendor_script_filename_cases() {
// Development mode scripts.
array(
'react-handle',
- 'https://unpkg.com/react@16.8.2/umd/react.development.js',
+ 'https://unpkg.com/react@16.6.3/umd/react.development.js',
'react-handle.HASH.js',
),
array(
'react-dom-handle',
- 'https://unpkg.com/react-dom@16.8.2/umd/react-dom.development.js',
+ 'https://unpkg.com/react-dom@16.6.3/umd/react-dom.development.js',
'react-dom-handle.HASH.js',
),
// Production mode scripts.
array(
'react-handle',
- 'https://unpkg.com/react@16.8.2/umd/react.production.min.js',
+ 'https://unpkg.com/react@16.6.3/umd/react.production.min.js',
'react-handle.min.HASH.js',
),
array(
'react-dom-handle',
- 'https://unpkg.com/react-dom@16.8.2/umd/react-dom.production.min.js',
+ 'https://unpkg.com/react-dom@16.6.3/umd/react-dom.production.min.js',
'react-dom-handle.min.HASH.js',
),
// Other cases.
From 211f65dd854fc1f7d7e54782863ad0629abeff41 Mon Sep 17 00:00:00 2001
From: Kjell Reigstad
Date: Thu, 21 Feb 2019 11:40:58 -0500
Subject: [PATCH 011/169] Cleanup URL Popover stylesheet. (#14015)
As noted in #13973, this stylesheet uses a relatively non-standard SCSS method of nesting some classnames. For instance:
`.editor-url-popover { &__settings-toggle { ... } }`.
... instead of:
`.editor-url-popover__settings-toggle { ... }`
This is different from the conventions used elsewhere in Gutenberg, and is a bit more difficult to follow for that reason.
This commit un-nests those styles, and should have no effect on the compiled CSS.
---
.../src/components/url-popover/style.scss | 84 +++++++++----------
1 file changed, 41 insertions(+), 43 deletions(-)
diff --git a/packages/editor/src/components/url-popover/style.scss b/packages/editor/src/components/url-popover/style.scss
index af277e9fc4c4a..51f16cadfb24c 100644
--- a/packages/editor/src/components/url-popover/style.scss
+++ b/packages/editor/src/components/url-popover/style.scss
@@ -1,61 +1,59 @@
-.editor-url-popover {
- &__row {
- display: flex;
- }
+.editor-url-popover__row {
+ display: flex;
+}
- // Any children of the popover-row that are not the settings-toggle
- // should take up as much space as possible.
- &__row > :not(.editor-url-popover__settings-toggle) {
- flex-grow: 1;
- }
+// Any children of the popover-row that are not the settings-toggle
+// should take up as much space as possible.
+.editor-url-popover__row > :not(.editor-url-popover__settings-toggle) {
+ flex-grow: 1;
+}
- // Mimic toolbar component styles for the icons in this popover.
- .components-icon-button {
- padding: 3px;
+// Mimic toolbar component styles for the icons in this popover.
+.editor-url-popover .components-icon-button {
+ padding: 3px;
- > svg {
- padding: 5px;
- border-radius: $radius-round-rectangle;
- height: 30px;
- width: 30px;
- }
+ > svg {
+ padding: 5px;
+ border-radius: $radius-round-rectangle;
+ height: 30px;
+ width: 30px;
+ }
- &:not(:disabled):not([aria-disabled="true"]):not(.is-default):hover {
- box-shadow: none;
+ &:not(:disabled):not([aria-disabled="true"]):not(.is-default):hover {
+ box-shadow: none;
- > svg {
- @include formatting-button-style__hover;
- }
+ > svg {
+ @include formatting-button-style__hover;
}
+ }
- &:not(:disabled):focus {
- box-shadow: none;
+ &:not(:disabled):focus {
+ box-shadow: none;
- > svg {
- @include formatting-button-style__focus;
- }
+ > svg {
+ @include formatting-button-style__focus;
}
}
+}
- &__settings-toggle {
- flex-shrink: 0;
+.editor-url-popover__settings-toggle {
+ flex-shrink: 0;
- // Add a left divider to the toggle button.
- border-radius: 0;
- border-left: $border-width solid $light-gray-500;
- margin-left: 1px;
+ // Add a left divider to the toggle button.
+ border-radius: 0;
+ border-left: $border-width solid $light-gray-500;
+ margin-left: 1px;
- &[aria-expanded="true"] .dashicon {
- transform: rotate(180deg);
- }
+ &[aria-expanded="true"] .dashicon {
+ transform: rotate(180deg);
}
+}
- &__settings {
- padding: $panel-padding;
- border-top: $border-width solid $light-gray-500;
+.editor-url-popover__settings {
+ padding: $panel-padding;
+ border-top: $border-width solid $light-gray-500;
- .components-base-control:last-child .components-base-control__field {
- margin-bottom: 0;
- }
+ .components-base-control:last-child .components-base-control__field {
+ margin-bottom: 0;
}
}
From 0c4f457b0bf2dd3b97a20edda216288c11b05466 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Fri, 22 Feb 2019 09:33:04 +0100
Subject: [PATCH 012/169] Add a generic block editor module (#13088)
---
.../developers/data/README.md | 3 +-
.../developers/data/data-core-block-editor.md | 1050 +++++
.../developers/data/data-core-editor.md | 1053 +----
docs/manifest.json | 14 +-
docs/tool/config.js | 7 +-
lib/client-assets.php | 1 +
lib/packages-dependencies.php | 10 +
package-lock.json | 16 +
package.json | 1 +
packages/block-editor/.npmrc | 1 +
packages/block-editor/CHANGELOG.md | 5 +
packages/block-editor/README.md | 13 +
packages/block-editor/package.json | 38 +
packages/block-editor/src/components/index.js | 1 +
.../src/components/provider/index.js | 152 +
packages/block-editor/src/index.js | 11 +
packages/block-editor/src/store/actions.js | 544 +++
.../src/store/array.js | 0
packages/block-editor/src/store/controls.js | 30 +
packages/block-editor/src/store/defaults.js | 133 +
packages/block-editor/src/store/effects.js | 150 +
packages/block-editor/src/store/index.js | 29 +
.../block-editor/src/store/middlewares.js | 45 +
packages/block-editor/src/store/reducer.js | 901 +++++
packages/block-editor/src/store/selectors.js | 1395 +++++++
.../block-editor/src/store/test/actions.js | 336 ++
.../src/store/test/array.js | 0
.../block-editor/src/store/test/effects.js | 280 ++
.../block-editor/src/store/test/reducer.js | 1721 ++++++++
.../block-editor/src/store/test/selectors.js | 2321 +++++++++++
.../data/src/plugins/persistence/index.js | 33 +-
.../specs/blocks/preformatted.test.js | 3 +
.../specs/plugins/container-blocks.test.js | 1 +
packages/edit-post/src/editor.js | 93 +-
packages/editor/package.json | 1 +
.../editor/src/components/block-list/block.js | 7 +-
.../src/components/post-text-editor/index.js | 6 +-
.../editor/src/components/provider/index.js | 135 +-
.../editor/src/components/rich-text/index.js | 10 +-
packages/editor/src/index.js | 1 +
packages/editor/src/store/actions.js | 554 +--
packages/editor/src/store/controls.js | 20 +-
packages/editor/src/store/defaults.js | 130 -
packages/editor/src/store/effects.js | 159 +-
.../src/store/effects/reusable-blocks.js | 41 +-
.../src/store/effects/test/reusable-blocks.js | 157 +-
packages/editor/src/store/reducer.js | 618 +--
packages/editor/src/store/selectors.js | 1844 ++-------
packages/editor/src/store/test/actions.js | 328 --
packages/editor/src/store/test/effects.js | 306 +-
packages/editor/src/store/test/reducer.js | 2143 ++--------
packages/editor/src/store/test/selectors.js | 3447 ++---------------
52 files changed, 10511 insertions(+), 9787 deletions(-)
create mode 100644 docs/designers-developers/developers/data/data-core-block-editor.md
create mode 100644 packages/block-editor/.npmrc
create mode 100644 packages/block-editor/CHANGELOG.md
create mode 100644 packages/block-editor/README.md
create mode 100644 packages/block-editor/package.json
create mode 100644 packages/block-editor/src/components/index.js
create mode 100644 packages/block-editor/src/components/provider/index.js
create mode 100644 packages/block-editor/src/index.js
create mode 100644 packages/block-editor/src/store/actions.js
rename packages/{editor => block-editor}/src/store/array.js (100%)
create mode 100644 packages/block-editor/src/store/controls.js
create mode 100644 packages/block-editor/src/store/defaults.js
create mode 100644 packages/block-editor/src/store/effects.js
create mode 100644 packages/block-editor/src/store/index.js
create mode 100644 packages/block-editor/src/store/middlewares.js
create mode 100644 packages/block-editor/src/store/reducer.js
create mode 100644 packages/block-editor/src/store/selectors.js
create mode 100644 packages/block-editor/src/store/test/actions.js
rename packages/{editor => block-editor}/src/store/test/array.js (100%)
create mode 100644 packages/block-editor/src/store/test/effects.js
create mode 100644 packages/block-editor/src/store/test/reducer.js
create mode 100644 packages/block-editor/src/store/test/selectors.js
diff --git a/docs/designers-developers/developers/data/README.md b/docs/designers-developers/developers/data/README.md
index 7ba7f9264e8bc..7408d171144cf 100644
--- a/docs/designers-developers/developers/data/README.md
+++ b/docs/designers-developers/developers/data/README.md
@@ -3,7 +3,8 @@
- [**core**: WordPress Core Data](/docs/designers-developers/developers/data/data-core.md)
- [**core/annotations**: Annotations](/docs/designers-developers/developers/data/data-core-annotations.md)
- [**core/blocks**: Block Types Data](/docs/designers-developers/developers/data/data-core-blocks.md)
- - [**core/editor**: The Editor’s Data](/docs/designers-developers/developers/data/data-core-editor.md)
+ - [**core/block-editor**: The Block Editor’s Data](/docs/designers-developers/developers/data/data-core-block-editor.md)
+ - [**core/editor**: The Post Editor’s Data](/docs/designers-developers/developers/data/data-core-editor.md)
- [**core/edit-post**: The Editor’s UI Data](/docs/designers-developers/developers/data/data-core-edit-post.md)
- [**core/notices**: Notices Data](/docs/designers-developers/developers/data/data-core-notices.md)
- [**core/nux**: The NUX (New User Experience) Data](/docs/designers-developers/developers/data/data-core-nux.md)
diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md
new file mode 100644
index 0000000000000..3166400bd3fbf
--- /dev/null
+++ b/docs/designers-developers/developers/data/data-core-block-editor.md
@@ -0,0 +1,1050 @@
+# **core/block-editor**: The Block Editor’s Data
+
+## Selectors
+
+### getBlockDependantsCacheBust
+
+Returns a new reference when the inner blocks of a given block client ID
+change. This is used exclusively as a memoized selector dependant, relying
+on this selector's shared return value and recursively those of its inner
+blocks defined as dependencies. This abuses mechanics of the selector
+memoization to return from the original selector function only when
+dependants change.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+### getBlockName
+
+Returns a block's name given its client ID, or null if no block exists with
+the client ID.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Block name.
+
+### isBlockValid
+
+Returns whether a block is valid or not.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Is Valid.
+
+### getBlockAttributes
+
+Returns a block's attributes given its client ID, or null if no block exists with
+the client ID.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Block attributes.
+
+### getBlock
+
+Returns a block given its client ID. This is a parsed copy of the block,
+containing its `blockName`, `clientId`, and current `attributes` state. This
+is not the block's registration settings, which must be retrieved from the
+blocks module registration store.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Parsed block object.
+
+### getBlocks
+
+Returns all block objects for the current post being edited as an array in
+the order they appear in the post.
+
+Note: It's important to memoize this selector to avoid return a new instance
+on each call
+
+*Parameters*
+
+ * state: Editor state.
+ * rootClientId: Optional root client ID of block list.
+
+*Returns*
+
+Post blocks.
+
+### getClientIdsOfDescendants
+
+Returns an array containing the clientIds of all descendants
+of the blocks given.
+
+*Parameters*
+
+ * state: Global application state.
+ * clientIds: Array of blocks to inspect.
+
+*Returns*
+
+ids of descendants.
+
+### getClientIdsWithDescendants
+
+Returns an array containing the clientIds of the top-level blocks
+and their descendants of any depth (for nested blocks).
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+ids of top-level and descendant blocks.
+
+### getGlobalBlockCount
+
+Returns the total number of blocks, or the total number of blocks with a specific name in a post.
+The number returned includes nested blocks.
+
+*Parameters*
+
+ * state: Global application state.
+ * blockName: Optional block name, if specified only blocks of that type will be counted.
+
+*Returns*
+
+Number of blocks in the post, or number of blocks with name equal to blockName.
+
+### getBlocksByClientId
+
+Given an array of block client IDs, returns the corresponding array of block
+objects.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientIds: Client IDs for which blocks are to be returned.
+
+*Returns*
+
+Block objects.
+
+### getBlockCount
+
+Returns the number of blocks currently present in the post.
+
+*Parameters*
+
+ * state: Editor state.
+ * rootClientId: Optional root client ID of block list.
+
+*Returns*
+
+Number of blocks in the post.
+
+### getBlockSelectionStart
+
+Returns the current block selection start. This value may be null, and it
+may represent either a singular block selection or multi-selection start.
+A selection is singular if its start and end match.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Client ID of block selection start.
+
+### getBlockSelectionEnd
+
+Returns the current block selection end. This value may be null, and it
+may represent either a singular block selection or multi-selection end.
+A selection is singular if its start and end match.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Client ID of block selection end.
+
+### getSelectedBlockCount
+
+Returns the number of blocks currently selected in the post.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Number of blocks selected in the post.
+
+### hasSelectedBlock
+
+Returns true if there is a single selected block, or false otherwise.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Whether a single block is selected.
+
+### getSelectedBlockClientId
+
+Returns the currently selected block client ID, or null if there is no
+selected block.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Selected block client ID.
+
+### getSelectedBlock
+
+Returns the currently selected block, or null if there is no selected block.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Selected block.
+
+### getBlockRootClientId
+
+Given a block client ID, returns the root block from which the block is
+nested, an empty string for top-level blocks, or null if the block does not
+exist.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block from which to find root client ID.
+
+*Returns*
+
+Root client ID, if exists
+
+### getBlockHierarchyRootClientId
+
+Given a block client ID, returns the root of the hierarchy from which the block is nested, return the block itself for root level blocks.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block from which to find root client ID.
+
+*Returns*
+
+Root client ID
+
+### getAdjacentBlockClientId
+
+Returns the client ID of the block adjacent one at the given reference
+startClientId and modifier directionality. Defaults start startClientId to
+the selected block, and direction as next block. Returns null if there is no
+adjacent block.
+
+*Parameters*
+
+ * state: Editor state.
+ * startClientId: Optional client ID of block from which to
+ search.
+ * modifier: Directionality multiplier (1 next, -1
+ previous).
+
+*Returns*
+
+Return the client ID of the block, or null if none exists.
+
+### getPreviousBlockClientId
+
+Returns the previous block's client ID from the given reference start ID.
+Defaults start to the selected block. Returns null if there is no previous
+block.
+
+*Parameters*
+
+ * state: Editor state.
+ * startClientId: Optional client ID of block from which to
+ search.
+
+*Returns*
+
+Adjacent block's client ID, or null if none exists.
+
+### getNextBlockClientId
+
+Returns the next block's client ID from the given reference start ID.
+Defaults start to the selected block. Returns null if there is no next
+block.
+
+*Parameters*
+
+ * state: Editor state.
+ * startClientId: Optional client ID of block from which to
+ search.
+
+*Returns*
+
+Adjacent block's client ID, or null if none exists.
+
+### getSelectedBlocksInitialCaretPosition
+
+Returns the initial caret position for the selected block.
+This position is to used to position the caret properly when the selected block changes.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Selected block.
+
+### getMultiSelectedBlockClientIds
+
+Returns the current multi-selection set of block client IDs, or an empty
+array if there is no multi-selection.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Multi-selected block client IDs.
+
+### getMultiSelectedBlocks
+
+Returns the current multi-selection set of blocks, or an empty array if
+there is no multi-selection.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Multi-selected block objects.
+
+### getFirstMultiSelectedBlockClientId
+
+Returns the client ID of the first block in the multi-selection set, or null
+if there is no multi-selection.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+First block client ID in the multi-selection set.
+
+### getLastMultiSelectedBlockClientId
+
+Returns the client ID of the last block in the multi-selection set, or null
+if there is no multi-selection.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Last block client ID in the multi-selection set.
+
+### isFirstMultiSelectedBlock
+
+Returns true if a multi-selection exists, and the block corresponding to the
+specified client ID is the first block of the multi-selection set, or false
+otherwise.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Whether block is first in multi-selection.
+
+### isBlockMultiSelected
+
+Returns true if the client ID occurs within the block multi-selection, or
+false otherwise.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Whether block is in multi-selection set.
+
+### isAncestorMultiSelected
+
+Returns true if an ancestor of the block is multi-selected, or false
+otherwise.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Whether an ancestor of the block is in multi-selection
+ set.
+
+### getMultiSelectedBlocksStartClientId
+
+Returns the client ID of the block which begins the multi-selection set, or
+null if there is no multi-selection.
+
+This is not necessarily the first client ID in the selection.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Client ID of block beginning multi-selection.
+
+### getMultiSelectedBlocksEndClientId
+
+Returns the client ID of the block which ends the multi-selection set, or
+null if there is no multi-selection.
+
+This is not necessarily the last client ID in the selection.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Client ID of block ending multi-selection.
+
+### getBlockOrder
+
+Returns an array containing all block client IDs in the editor in the order
+they appear. Optionally accepts a root client ID of the block list for which
+the order should be returned, defaulting to the top-level block order.
+
+*Parameters*
+
+ * state: Editor state.
+ * rootClientId: Optional root client ID of block list.
+
+*Returns*
+
+Ordered client IDs of editor blocks.
+
+### getBlockIndex
+
+Returns the index at which the block corresponding to the specified client
+ID occurs within the block order, or `-1` if the block does not exist.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+ * rootClientId: Optional root client ID of block list.
+
+*Returns*
+
+Index at which block exists in order.
+
+### isBlockSelected
+
+Returns true if the block corresponding to the specified client ID is
+currently selected and no multi-selection exists, or false otherwise.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Whether block is selected and multi-selection exists.
+
+### hasSelectedInnerBlock
+
+Returns true if one of the block's inner blocks is selected.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+ * deep: Perform a deep check.
+
+*Returns*
+
+Whether the block as an inner block selected
+
+### isBlockWithinSelection
+
+Returns true if the block corresponding to the specified client ID is
+currently selected but isn't the last of the selected blocks. Here "last"
+refers to the block sequence in the document, _not_ the sequence of
+multi-selection, which is why `state.blockSelection.end` isn't used.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Whether block is selected and not the last in the
+ selection.
+
+### hasMultiSelection
+
+Returns true if a multi-selection has been made, or false otherwise.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Whether multi-selection has been made.
+
+### isMultiSelecting
+
+Whether in the process of multi-selecting or not. This flag is only true
+while the multi-selection is being selected (by mouse move), and is false
+once the multi-selection has been settled.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+True if multi-selecting, false if not.
+
+### isSelectionEnabled
+
+Selector that returns if multi-selection is enabled or not.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+True if it should be possible to multi-select blocks, false if multi-selection is disabled.
+
+### getBlockMode
+
+Returns the block's editing mode, defaulting to "visual" if not explicitly
+assigned.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Block editing mode.
+
+### isTyping
+
+Returns true if the user is typing, or false otherwise.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Whether user is typing.
+
+### isCaretWithinFormattedText
+
+Returns true if the caret is within formatted text, or false otherwise.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Whether the caret is within formatted text.
+
+### getBlockInsertionPoint
+
+Returns the insertion point, the index at which the new inserted block would
+be placed. Defaults to the last index.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+Insertion point object with `rootClientId`, `index`.
+
+### isBlockInsertionPointVisible
+
+Returns true if we should show the block insertion point.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Whether the insertion point is visible or not.
+
+### isValidTemplate
+
+Returns whether the blocks matches the template or not.
+
+*Parameters*
+
+ * state: null
+
+*Returns*
+
+Whether the template is valid or not.
+
+### getTemplate
+
+Returns the defined block template
+
+*Parameters*
+
+ * state: null
+
+*Returns*
+
+Block Template
+
+### getTemplateLock
+
+Returns the defined block template lock. Optionally accepts a root block
+client ID as context, otherwise defaulting to the global context.
+
+*Parameters*
+
+ * state: Editor state.
+ * rootClientId: Optional block root client ID.
+
+*Returns*
+
+Block Template Lock
+
+### canInsertBlockType
+
+Determines if the given block type is allowed to be inserted into the block list.
+
+*Parameters*
+
+ * state: Editor state.
+ * blockName: The name of the block type, e.g.' core/paragraph'.
+ * rootClientId: Optional root client ID of block list.
+
+*Returns*
+
+Whether the given block type is allowed to be inserted.
+
+### getInserterItems
+
+Determines the items that appear in the inserter. Includes both static
+items (e.g. a regular block type) and dynamic items (e.g. a reusable block).
+
+Each item object contains what's necessary to display a button in the
+inserter and handle its selection.
+
+The 'utility' property indicates how useful we think an item will be to the
+user. There are 4 levels of utility:
+
+1. Blocks that are contextually useful (utility = 3)
+2. Blocks that have been previously inserted (utility = 2)
+3. Blocks that are in the common category (utility = 1)
+4. All other blocks (utility = 0)
+
+The 'frecency' property is a heuristic (https://en.wikipedia.org/wiki/Frecency)
+that combines block usage frequenty and recency.
+
+Items are returned ordered descendingly by their 'utility' and 'frecency'.
+
+*Parameters*
+
+ * state: Editor state.
+ * rootClientId: Optional root client ID of block list.
+
+*Returns*
+
+Items that appear in inserter.
+
+### hasInserterItems
+
+Determines whether there are items to show in the inserter.
+
+*Parameters*
+
+ * state: Editor state.
+ * rootClientId: Optional root client ID of block list.
+
+*Returns*
+
+Items that appear in inserter.
+
+### getBlockListSettings
+
+Returns the Block List settings of a block, if any exist.
+
+*Parameters*
+
+ * state: Editor state.
+ * clientId: Block client ID.
+
+*Returns*
+
+Block settings of the block if set.
+
+### getEditorSettings
+
+Returns the editor settings.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+The editor settings object.
+
+### isLastBlockChangePersistent
+
+Returns true if the most recent block change is be considered persistent, or
+false otherwise. A persistent change is one committed by BlockEditorProvider
+via its `onChange` callback, in addition to `onInput`.
+
+*Parameters*
+
+ * state: Block editor state.
+
+*Returns*
+
+Whether the most recent block change was persistent.
+
+## Actions
+
+### resetBlocks
+
+Returns an action object used in signalling that blocks state should be
+reset to the specified array of blocks, taking precedence over any other
+content reflected as an edit in state.
+
+*Parameters*
+
+ * blocks: Array of blocks.
+
+### receiveBlocks
+
+Returns an action object used in signalling that blocks have been received.
+Unlike resetBlocks, these should be appended to the existing known set, not
+replacing.
+
+*Parameters*
+
+ * blocks: Array of block objects.
+
+### updateBlockAttributes
+
+Returns an action object used in signalling that the block attributes with
+the specified client ID has been updated.
+
+*Parameters*
+
+ * clientId: Block client ID.
+ * attributes: Block attributes to be merged.
+
+### updateBlock
+
+Returns an action object used in signalling that the block with the
+specified client ID has been updated.
+
+*Parameters*
+
+ * clientId: Block client ID.
+ * updates: Block attributes to be merged.
+
+### selectBlock
+
+Returns an action object used in signalling that the block with the
+specified client ID has been selected, optionally accepting a position
+value reflecting its selection directionality. An initialPosition of -1
+reflects a reverse selection.
+
+*Parameters*
+
+ * clientId: Block client ID.
+ * initialPosition: Optional initial position. Pass as -1 to
+ reflect reverse selection.
+
+### selectPreviousBlock
+
+Yields action objects used in signalling that the block preceding the given
+clientId should be selected.
+
+*Parameters*
+
+ * clientId: Block client ID.
+
+### selectNextBlock
+
+Yields action objects used in signalling that the block following the given
+clientId should be selected.
+
+*Parameters*
+
+ * clientId: Block client ID.
+
+### startMultiSelect
+
+Returns an action object used in signalling that a block multi-selection has started.
+
+### stopMultiSelect
+
+Returns an action object used in signalling that block multi-selection stopped.
+
+### multiSelect
+
+Returns an action object used in signalling that block multi-selection changed.
+
+*Parameters*
+
+ * start: First block of the multi selection.
+ * end: Last block of the multiselection.
+
+### clearSelectedBlock
+
+Returns an action object used in signalling that the block selection is cleared.
+
+### toggleSelection
+
+Returns an action object that enables or disables block selection.
+
+*Parameters*
+
+ * boolean: [isSelectionEnabled=true] Whether block selection should
+ be enabled.
+
+### replaceBlocks
+
+Returns an action object signalling that a blocks should be replaced with
+one or more replacement blocks.
+
+*Parameters*
+
+ * clientIds: Block client ID(s) to replace.
+ * blocks: Replacement block(s).
+
+### replaceBlock
+
+Returns an action object signalling that a single block should be replaced
+with one or more replacement blocks.
+
+*Parameters*
+
+ * clientId: Block client ID to replace.
+ * block: Replacement block(s).
+
+### moveBlockToPosition
+
+Returns an action object signalling that an indexed block should be moved
+to a new index.
+
+*Parameters*
+
+ * clientId: The client ID of the block.
+ * fromRootClientId: Root client ID source.
+ * toRootClientId: Root client ID destination.
+ * index: The index to move the block into.
+
+### insertBlock
+
+Returns an action object used in signalling that a single block should be
+inserted, optionally at a specific index respective a root block list.
+
+*Parameters*
+
+ * block: Block object to insert.
+ * index: Index at which block should be inserted.
+ * rootClientId: Optional root client ID of block list on which to insert.
+ * updateSelection: If true block selection will be updated. If false, block selection will not change. Defaults to true.
+
+### insertBlocks
+
+Returns an action object used in signalling that an array of blocks should
+be inserted, optionally at a specific index respective a root block list.
+
+*Parameters*
+
+ * blocks: Block objects to insert.
+ * index: Index at which block should be inserted.
+ * rootClientId: Optional root client ID of block list on which to insert.
+ * updateSelection: If true block selection will be updated. If false, block selection will not change. Defaults to true.
+
+### showInsertionPoint
+
+Returns an action object used in signalling that the insertion point should
+be shown.
+
+*Parameters*
+
+ * rootClientId: Optional root client ID of block list on
+ which to insert.
+ * index: Index at which block should be inserted.
+
+### hideInsertionPoint
+
+Returns an action object hiding the insertion point.
+
+### setTemplateValidity
+
+Returns an action object resetting the template validity.
+
+*Parameters*
+
+ * isValid: template validity flag.
+
+### synchronizeTemplate
+
+Returns an action object synchronize the template with the list of blocks
+
+### mergeBlocks
+
+Returns an action object used in signalling that two blocks should be merged
+
+*Parameters*
+
+ * firstBlockClientId: Client ID of the first block to merge.
+ * secondBlockClientId: Client ID of the second block to merge.
+
+### removeBlocks
+
+Yields action objects used in signalling that the blocks corresponding to
+the set of specified client IDs are to be removed.
+
+*Parameters*
+
+ * clientIds: Client IDs of blocks to remove.
+ * selectPrevious: True if the previous block should be
+ selected when a block is removed.
+
+### removeBlock
+
+Returns an action object used in signalling that the block with the
+specified client ID is to be removed.
+
+*Parameters*
+
+ * clientId: Client ID of block to remove.
+ * selectPrevious: True if the previous block should be
+ selected when a block is removed.
+
+### toggleBlockMode
+
+Returns an action object used to toggle the block editing mode between
+visual and HTML modes.
+
+*Parameters*
+
+ * clientId: Block client ID.
+
+### startTyping
+
+Returns an action object used in signalling that the user has begun to type.
+
+### stopTyping
+
+Returns an action object used in signalling that the user has stopped typing.
+
+### enterFormattedText
+
+Returns an action object used in signalling that the caret has entered formatted text.
+
+### exitFormattedText
+
+Returns an action object used in signalling that the user caret has exited formatted text.
+
+### insertDefaultBlock
+
+Returns an action object used in signalling that a new block of the default
+type should be added to the block list.
+
+*Parameters*
+
+ * attributes: Optional attributes of the block to assign.
+ * rootClientId: Optional root client ID of block list on which
+ to append.
+ * index: Optional index where to insert the default block
+
+### updateBlockListSettings
+
+Returns an action object that changes the nested settings of a given block.
+
+*Parameters*
+
+ * clientId: Client ID of the block whose nested setting are
+ being received.
+ * settings: Object with the new settings for the nested block.
+
+### updateEditorSettings
+
+Returns an action object used in signalling that the editor settings have been updated.
+
+*Parameters*
+
+ * settings: Updated settings
+
+### __unstableSaveReusableBlock
+
+Returns an action object used in signalling that a temporary reusable blocks have been saved
+in order to switch its temporary id with the real id.
+
+*Parameters*
+
+ * id: Reusable block's id.
+ * updatedId: Updated block's id.
+
+### __unstableMarkLastChangeAsPersistent
+
+Returns an action object used in signalling that the last block change should be marked explicitely as persistent.
\ No newline at end of file
diff --git a/docs/designers-developers/developers/data/data-core-editor.md b/docs/designers-developers/developers/data/data-core-editor.md
index 96e65b41659de..7e95832dc5bdb 100644
--- a/docs/designers-developers/developers/data/data-core-editor.md
+++ b/docs/designers-developers/developers/data/data-core-editor.md
@@ -1,4 +1,4 @@
-# **core/editor**: The Editor’s Data
+# **core/editor**: The Post Editor’s Data
## Selectors
@@ -365,676 +365,6 @@ and modified date are the same.
Whether the edited post has a floating date value.
-### getBlockDependantsCacheBust
-
-Returns a new reference when the inner blocks of a given block client ID
-change. This is used exclusively as a memoized selector dependant, relying
-on this selector's shared return value and recursively those of its inner
-blocks defined as dependencies. This abuses mechanics of the selector
-memoization to return from the original selector function only when
-dependants change.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-A value whose reference will change only when inner blocks of
- the given block client ID change.
-
-### getBlockName
-
-Returns a block's name given its client ID, or null if no block exists with
-the client ID.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Block name.
-
-### isBlockValid
-
-Returns whether a block is valid or not.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Is Valid.
-
-### getBlockAttributes
-
-Returns a block's attributes given its client ID, or null if no block exists with
-the client ID.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Block attributes.
-
-### getBlock
-
-Returns a block given its client ID. This is a parsed copy of the block,
-containing its `blockName`, `clientId`, and current `attributes` state. This
-is not the block's registration settings, which must be retrieved from the
-blocks module registration store.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Parsed block object.
-
-### getBlocks
-
-Returns all block objects for the current post being edited as an array in
-the order they appear in the post.
-
-Note: It's important to memoize this selector to avoid return a new instance
-on each call
-
-*Parameters*
-
- * state: Editor state.
- * rootClientId: Optional root client ID of block list.
-
-*Returns*
-
-Post blocks.
-
-### getClientIdsOfDescendants
-
-Returns an array containing the clientIds of all descendants
-of the blocks given.
-
-*Parameters*
-
- * state: Global application state.
- * clientIds: Array of blocks to inspect.
-
-*Returns*
-
-ids of descendants.
-
-### getClientIdsWithDescendants
-
-Returns an array containing the clientIds of the top-level blocks
-and their descendants of any depth (for nested blocks).
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-ids of top-level and descendant blocks.
-
-### getGlobalBlockCount
-
-Returns the total number of blocks, or the total number of blocks with a specific name in a post.
-The number returned includes nested blocks.
-
-*Parameters*
-
- * state: Global application state.
- * blockName: Optional block name, if specified only blocks of that type will be counted.
-
-*Returns*
-
-Number of blocks in the post, or number of blocks with name equal to blockName.
-
-### getBlocksByClientId
-
-Given an array of block client IDs, returns the corresponding array of block
-objects.
-
-*Parameters*
-
- * state: Editor state.
- * clientIds: Client IDs for which blocks are to be returned.
-
-*Returns*
-
-Block objects.
-
-### getBlockCount
-
-Returns the number of blocks currently present in the post.
-
-*Parameters*
-
- * state: Editor state.
- * rootClientId: Optional root client ID of block list.
-
-*Returns*
-
-Number of blocks in the post.
-
-### getBlockSelectionStart
-
-Returns the current block selection start. This value may be null, and it
-may represent either a singular block selection or multi-selection start.
-A selection is singular if its start and end match.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Client ID of block selection start.
-
-### getBlockSelectionEnd
-
-Returns the current block selection end. This value may be null, and it
-may represent either a singular block selection or multi-selection end.
-A selection is singular if its start and end match.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Client ID of block selection end.
-
-### getSelectedBlockCount
-
-Returns the number of blocks currently selected in the post.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Number of blocks selected in the post.
-
-### hasSelectedBlock
-
-Returns true if there is a single selected block, or false otherwise.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Whether a single block is selected.
-
-### getSelectedBlockClientId
-
-Returns the currently selected block client ID, or null if there is no
-selected block.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Selected block client ID.
-
-### getSelectedBlock
-
-Returns the currently selected block, or null if there is no selected block.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Selected block.
-
-### getBlockRootClientId
-
-Given a block client ID, returns the root block from which the block is
-nested, an empty string for top-level blocks, or null if the block does not
-exist.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block from which to find root client ID.
-
-*Returns*
-
-Root client ID, if exists
-
-### getBlockHierarchyRootClientId
-
-Given a block client ID, returns the root of the hierarchy from which the block is nested, return the block itself for root level blocks.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block from which to find root client ID.
-
-*Returns*
-
-Root client ID
-
-### getAdjacentBlockClientId
-
-Returns the client ID of the block adjacent one at the given reference
-startClientId and modifier directionality. Defaults start startClientId to
-the selected block, and direction as next block. Returns null if there is no
-adjacent block.
-
-*Parameters*
-
- * state: Editor state.
- * startClientId: Optional client ID of block from which to
- search.
- * modifier: Directionality multiplier (1 next, -1
- previous).
-
-*Returns*
-
-Return the client ID of the block, or null if none exists.
-
-### getPreviousBlockClientId
-
-Returns the previous block's client ID from the given reference start ID.
-Defaults start to the selected block. Returns null if there is no previous
-block.
-
-*Parameters*
-
- * state: Editor state.
- * startClientId: Optional client ID of block from which to
- search.
-
-*Returns*
-
-Adjacent block's client ID, or null if none exists.
-
-### getNextBlockClientId
-
-Returns the next block's client ID from the given reference start ID.
-Defaults start to the selected block. Returns null if there is no next
-block.
-
-*Parameters*
-
- * state: Editor state.
- * startClientId: Optional client ID of block from which to
- search.
-
-*Returns*
-
-Adjacent block's client ID, or null if none exists.
-
-### getSelectedBlocksInitialCaretPosition
-
-Returns the initial caret position for the selected block.
-This position is to used to position the caret properly when the selected block changes.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Selected block.
-
-### getMultiSelectedBlockClientIds
-
-Returns the current multi-selection set of block client IDs, or an empty
-array if there is no multi-selection.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Multi-selected block client IDs.
-
-### getMultiSelectedBlocks
-
-Returns the current multi-selection set of blocks, or an empty array if
-there is no multi-selection.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Multi-selected block objects.
-
-### getFirstMultiSelectedBlockClientId
-
-Returns the client ID of the first block in the multi-selection set, or null
-if there is no multi-selection.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-First block client ID in the multi-selection set.
-
-### getLastMultiSelectedBlockClientId
-
-Returns the client ID of the last block in the multi-selection set, or null
-if there is no multi-selection.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Last block client ID in the multi-selection set.
-
-### isFirstMultiSelectedBlock
-
-Returns true if a multi-selection exists, and the block corresponding to the
-specified client ID is the first block of the multi-selection set, or false
-otherwise.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Whether block is first in multi-selection.
-
-### isBlockMultiSelected
-
-Returns true if the client ID occurs within the block multi-selection, or
-false otherwise.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Whether block is in multi-selection set.
-
-### isAncestorMultiSelected
-
-Returns true if an ancestor of the block is multi-selected, or false
-otherwise.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Whether an ancestor of the block is in multi-selection
- set.
-
-### getMultiSelectedBlocksStartClientId
-
-Returns the client ID of the block which begins the multi-selection set, or
-null if there is no multi-selection.
-
-This is not necessarily the first client ID in the selection.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Client ID of block beginning multi-selection.
-
-### getMultiSelectedBlocksEndClientId
-
-Returns the client ID of the block which ends the multi-selection set, or
-null if there is no multi-selection.
-
-This is not necessarily the last client ID in the selection.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Client ID of block ending multi-selection.
-
-### getBlockOrder
-
-Returns an array containing all block client IDs in the editor in the order
-they appear. Optionally accepts a root client ID of the block list for which
-the order should be returned, defaulting to the top-level block order.
-
-*Parameters*
-
- * state: Editor state.
- * rootClientId: Optional root client ID of block list.
-
-*Returns*
-
-Ordered client IDs of editor blocks.
-
-### getBlockIndex
-
-Returns the index at which the block corresponding to the specified client
-ID occurs within the block order, or `-1` if the block does not exist.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
- * rootClientId: Optional root client ID of block list.
-
-*Returns*
-
-Index at which block exists in order.
-
-### isBlockSelected
-
-Returns true if the block corresponding to the specified client ID is
-currently selected and no multi-selection exists, or false otherwise.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Whether block is selected and multi-selection exists.
-
-### hasSelectedInnerBlock
-
-Returns true if one of the block's inner blocks is selected.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
- * deep: Perform a deep check.
-
-*Returns*
-
-Whether the block as an inner block selected
-
-### isBlockWithinSelection
-
-Returns true if the block corresponding to the specified client ID is
-currently selected but isn't the last of the selected blocks. Here "last"
-refers to the block sequence in the document, _not_ the sequence of
-multi-selection, which is why `state.blockSelection.end` isn't used.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Whether block is selected and not the last in the
- selection.
-
-### hasMultiSelection
-
-Returns true if a multi-selection has been made, or false otherwise.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Whether multi-selection has been made.
-
-### isMultiSelecting
-
-Whether in the process of multi-selecting or not. This flag is only true
-while the multi-selection is being selected (by mouse move), and is false
-once the multi-selection has been settled.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-True if multi-selecting, false if not.
-
-### isSelectionEnabled
-
-Selector that returns if multi-selection is enabled or not.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-True if it should be possible to multi-select blocks, false if multi-selection is disabled.
-
-### getBlockMode
-
-Returns the block's editing mode, defaulting to "visual" if not explicitly
-assigned.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Block editing mode.
-
-### isTyping
-
-Returns true if the user is typing, or false otherwise.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Whether user is typing.
-
-### isCaretWithinFormattedText
-
-Returns true if the caret is within formatted text, or false otherwise.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Whether the caret is within formatted text.
-
-### getBlockInsertionPoint
-
-Returns the insertion point, the index at which the new inserted block would
-be placed. Defaults to the last index.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-Insertion point object with `rootClientId`, `index`.
-
-### isBlockInsertionPointVisible
-
-Returns true if we should show the block insertion point.
-
-*Parameters*
-
- * state: Global application state.
-
-*Returns*
-
-Whether the insertion point is visible or not.
-
-### isValidTemplate
-
-Returns whether the blocks matches the template or not.
-
-*Parameters*
-
- * state: null
-
-*Returns*
-
-Whether the template is valid or not.
-
-### getTemplate
-
-Returns the defined block template
-
-*Parameters*
-
- * state: null
-
-*Returns*
-
-Block Template
-
-### getTemplateLock
-
-Returns the defined block template lock. Optionally accepts a root block
-client ID as context, otherwise defaulting to the global context.
-
-*Parameters*
-
- * state: Editor state.
- * rootClientId: Optional block root client ID.
-
-*Returns*
-
-Block Template Lock
-
### isSavingPost
Returns true if the post is currently being saved, or false otherwise.
@@ -1149,63 +479,6 @@ before falling back to serialization of block state.
Post content.
-### canInsertBlockType
-
-Determines if the given block type is allowed to be inserted into the block list.
-
-*Parameters*
-
- * state: Editor state.
- * blockName: The name of the block type, e.g.' core/paragraph'.
- * rootClientId: Optional root client ID of block list.
-
-*Returns*
-
-Whether the given block type is allowed to be inserted.
-
-### getInserterItems
-
-Determines the items that appear in the inserter. Includes both static
-items (e.g. a regular block type) and dynamic items (e.g. a reusable block).
-
-Each item object contains what's necessary to display a button in the
-inserter and handle its selection.
-
-The 'utility' property indicates how useful we think an item will be to the
-user. There are 4 levels of utility:
-
-1. Blocks that are contextually useful (utility = 3)
-2. Blocks that have been previously inserted (utility = 2)
-3. Blocks that are in the common category (utility = 1)
-4. All other blocks (utility = 0)
-
-The 'frecency' property is a heuristic (https://en.wikipedia.org/wiki/Frecency)
-that combines block usage frequenty and recency.
-
-Items are returned ordered descendingly by their 'utility' and 'frecency'.
-
-*Parameters*
-
- * state: Editor state.
- * rootClientId: Optional root client ID of block list.
-
-*Returns*
-
-Items that appear in inserter.
-
-### hasInserterItems
-
-Determines whether there are items to show in the inserter.
-
-*Parameters*
-
- * state: Editor state.
- * rootClientId: Optional root client ID of block list.
-
-*Returns*
-
-Items that appear in inserter.
-
### __experimentalGetReusableBlock
Returns the reusable block with the given ID.
@@ -1336,44 +609,6 @@ before state satisfies the given predicate function.
Whether predicate matches for some history.
-### getBlockListSettings
-
-Returns the Block List settings of a block, if any exist.
-
-*Parameters*
-
- * state: Editor state.
- * clientId: Block client ID.
-
-*Returns*
-
-Block settings of the block if set.
-
-### getEditorSettings
-
-Returns the editor settings.
-
-*Parameters*
-
- * state: Editor state.
-
-*Returns*
-
-The editor settings object.
-
-### getTokenSettings
-
-Returns the token settings.
-
-*Parameters*
-
- * state: Editor state.
- * name: Token name.
-
-*Returns*
-
-Token settings object, or the named token settings object if set.
-
### isPostLocked
Returns whether the post is locked.
@@ -1459,6 +694,30 @@ or skipped when the user clicks the "publish" button.
Whether the pre-publish panel should be shown or not.
+### getEditorBlocks
+
+Return the current block list.
+
+*Parameters*
+
+ * state: null
+
+*Returns*
+
+Block list.
+
+### __unstableIsEditorReady
+
+Is the editor ready
+
+*Parameters*
+
+ * state: null
+
+*Returns*
+
+is Ready.
+
## Actions
### setupEditor
@@ -1470,6 +729,7 @@ the specified post object and editor settings.
* post: Post object.
* edits: Initial edited attributes object.
+ * template: Block Template.
### resetPost
@@ -1505,170 +765,6 @@ Returns an action object used to setup the editor state when first opening an ed
*Parameters*
* post: Post object.
- * blocks: Array of blocks.
-
-### resetBlocks
-
-Returns an action object used in signalling that blocks state should be
-reset to the specified array of blocks, taking precedence over any other
-content reflected as an edit in state.
-
-*Parameters*
-
- * blocks: Array of blocks.
-
-### receiveBlocks
-
-Returns an action object used in signalling that blocks have been received.
-Unlike resetBlocks, these should be appended to the existing known set, not
-replacing.
-
-*Parameters*
-
- * blocks: Array of block objects.
-
-### updateBlockAttributes
-
-Returns an action object used in signalling that the block attributes with
-the specified client ID has been updated.
-
-*Parameters*
-
- * clientId: Block client ID.
- * attributes: Block attributes to be merged.
-
-### updateBlock
-
-Returns an action object used in signalling that the block with the
-specified client ID has been updated.
-
-*Parameters*
-
- * clientId: Block client ID.
- * updates: Block attributes to be merged.
-
-### selectBlock
-
-Returns an action object used in signalling that the block with the
-specified client ID has been selected, optionally accepting a position
-value reflecting its selection directionality. An initialPosition of -1
-reflects a reverse selection.
-
-*Parameters*
-
- * clientId: Block client ID.
- * initialPosition: Optional initial position. Pass as -1 to
- reflect reverse selection.
-
-### selectPreviousBlock
-
-Yields action objects used in signalling that the block preceding the given
-clientId should be selected.
-
-*Parameters*
-
- * clientId: Block client ID.
-
-### selectNextBlock
-
-Yields action objects used in signalling that the block following the given
-clientId should be selected.
-
-*Parameters*
-
- * clientId: Block client ID.
-
-### toggleSelection
-
-Returns an action object that enables or disables block selection.
-
-*Parameters*
-
- * boolean: [isSelectionEnabled=true] Whether block selection should
- be enabled.
-
-### replaceBlocks
-
-Returns an action object signalling that a blocks should be replaced with
-one or more replacement blocks.
-
-*Parameters*
-
- * clientIds: Block client ID(s) to replace.
- * blocks: Replacement block(s).
-
-### replaceBlock
-
-Returns an action object signalling that a single block should be replaced
-with one or more replacement blocks.
-
-*Parameters*
-
- * clientId: Block client ID to replace.
- * block: Replacement block(s).
-
-### moveBlockToPosition
-
-Returns an action object signalling that an indexed block should be moved
-to a new index.
-
-*Parameters*
-
- * clientId: The client ID of the block.
- * fromRootClientId: Root client ID source.
- * toRootClientId: Root client ID destination.
- * index: The index to move the block into.
-
-### insertBlock
-
-Returns an action object used in signalling that a single block should be
-inserted, optionally at a specific index respective a root block list.
-
-*Parameters*
-
- * block: Block object to insert.
- * index: Index at which block should be inserted.
- * rootClientId: Optional root client ID of block list on which to insert.
- * updateSelection: If true block selection will be updated. If false, block selection will not change. Defaults to true.
-
-### insertBlocks
-
-Returns an action object used in signalling that an array of blocks should
-be inserted, optionally at a specific index respective a root block list.
-
-*Parameters*
-
- * blocks: Block objects to insert.
- * index: Index at which block should be inserted.
- * rootClientId: Optional root client ID of block list on which to insert.
- * updateSelection: If true block selection will be updated. If false, block selection will not change. Defaults to true.
-
-### showInsertionPoint
-
-Returns an action object used in signalling that the insertion point should
-be shown.
-
-*Parameters*
-
- * rootClientId: Optional root client ID of block list on
- which to insert.
- * index: Index at which block should be inserted.
-
-### hideInsertionPoint
-
-Returns an action object hiding the insertion point.
-
-### setTemplateValidity
-
-Returns an action object resetting the template validity.
-
-*Parameters*
-
- * isValid: template validity flag.
-
-### synchronizeTemplate
-
-Returns an action object synchronize the template with the list of blocks
### editPost
@@ -1688,15 +784,6 @@ Returns an action object to save the post.
* options: Options for the save.
* options.isAutosave: Perform an autosave if true.
-### mergeBlocks
-
-Returns an action object used in signalling that two blocks should be merged
-
-*Parameters*
-
- * firstBlockClientId: Client ID of the first block to merge.
- * secondBlockClientId: Client ID of the second block to merge.
-
### autosave
Returns an action object used in signalling that the post should autosave.
@@ -1719,53 +806,6 @@ Returns an action object used in signalling that undo history should pop.
Returns an action object used in signalling that undo history record should
be created.
-### removeBlocks
-
-Yields action objects used in signalling that the blocks corresponding to
-the set of specified client IDs are to be removed.
-
-*Parameters*
-
- * clientIds: Client IDs of blocks to remove.
- * selectPrevious: True if the previous block should be
- selected when a block is removed.
-
-### removeBlock
-
-Returns an action object used in signalling that the block with the
-specified client ID is to be removed.
-
-*Parameters*
-
- * clientId: Client ID of block to remove.
- * selectPrevious: True if the previous block should be
- selected when a block is removed.
-
-### toggleBlockMode
-
-Returns an action object used to toggle the block editing mode between
-visual and HTML modes.
-
-*Parameters*
-
- * clientId: Block client ID.
-
-### startTyping
-
-Returns an action object used in signalling that the user has begun to type.
-
-### stopTyping
-
-Returns an action object used in signalling that the user has stopped typing.
-
-### enterFormattedText
-
-Returns an action object used in signalling that the caret has entered formatted text.
-
-### exitFormattedText
-
-Returns an action object used in signalling that the user caret has exited formatted text.
-
### updatePostLock
Returns an action object used to lock the editor.
@@ -1838,36 +878,6 @@ Returns an action object used to convert a static block into a reusable block.
* clientIds: The client IDs of the block to detach.
-### insertDefaultBlock
-
-Returns an action object used in signalling that a new block of the default
-type should be added to the block list.
-
-*Parameters*
-
- * attributes: Optional attributes of the block to assign.
- * rootClientId: Optional root client ID of block list on which
- to append.
- * index: Optional index where to insert the default block
-
-### updateBlockListSettings
-
-Returns an action object that changes the nested settings of a given block.
-
-*Parameters*
-
- * clientId: Client ID of the block whose nested setting are
- being received.
- * settings: Object with the new settings for the nested block.
-
-### updateEditorSettings
-
-Returns an action object used in signalling that the editor settings have been updated.
-
-*Parameters*
-
- * settings: Updated settings
-
### enablePublishSidebar
Returns an action object used in signalling that the user has enabled the publish sidebar.
@@ -1890,4 +900,13 @@ Returns an action object used to signal that post saving is unlocked.
*Parameters*
- * lockName: The lock name.
\ No newline at end of file
+ * lockName: The lock name.
+
+### resetEditorBlocks
+
+Returns an action object used to signal that the blocks have been updated.
+
+*Parameters*
+
+ * blocks: Block Array.
+ * options: Optional options.
\ No newline at end of file
diff --git a/docs/manifest.json b/docs/manifest.json
index 026790426f1ec..c6eccbce1d73d 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -557,6 +557,12 @@
"markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/blob/README.md",
"parent": "packages"
},
+ {
+ "title": "@wordpress/block-editor",
+ "slug": "packages-block-editor",
+ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/block-editor/README.md",
+ "parent": "packages"
+ },
{
"title": "@wordpress/block-library",
"slug": "packages-block-library",
@@ -1248,7 +1254,13 @@
"parent": "data"
},
{
- "title": "The Editor’s Data",
+ "title": "The Block Editor’s Data",
+ "slug": "data-core-block-editor",
+ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/data/data-core-block-editor.md",
+ "parent": "data"
+ },
+ {
+ "title": "The Post Editor’s Data",
"slug": "data-core-editor",
"markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/data/data-core-editor.md",
"parent": "data"
diff --git a/docs/tool/config.js b/docs/tool/config.js
index 87900c4c624fa..6dd2341e3d3c8 100644
--- a/docs/tool/config.js
+++ b/docs/tool/config.js
@@ -25,8 +25,13 @@ module.exports = {
selectors: [ path.resolve( root, 'packages/blocks/src/store/selectors.js' ) ],
actions: [ path.resolve( root, 'packages/blocks/src/store/actions.js' ) ],
},
+ 'core/block-editor': {
+ title: 'The Block Editor’s Data',
+ selectors: [ path.resolve( root, 'packages/block-editor/src/store/selectors.js' ) ],
+ actions: [ path.resolve( root, 'packages/block-editor/src/store/actions.js' ) ],
+ },
'core/editor': {
- title: 'The Editor’s Data',
+ title: 'The Post Editor’s Data',
selectors: [ path.resolve( root, 'packages/editor/src/store/selectors.js' ) ],
actions: [ path.resolve( root, 'packages/editor/src/store/actions.js' ) ],
},
diff --git a/lib/client-assets.php b/lib/client-assets.php
index b34e428425e80..72d3522075a01 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -205,6 +205,7 @@ function gutenberg_register_scripts_and_styles() {
' wp.data',
' .use( wp.data.plugins.persistence, { storageKey: storageKey } )',
' .use( wp.data.plugins.controls );',
+ ' wp.data.plugins.persistence.__unstableMigrate( { storageKey: storageKey } );',
'} )()',
)
)
diff --git a/lib/packages-dependencies.php b/lib/packages-dependencies.php
index 9da875e8df2cc..e2f135bda24d9 100644
--- a/lib/packages-dependencies.php
+++ b/lib/packages-dependencies.php
@@ -44,6 +44,15 @@
),
'wp-block-serialization-default-parser' => array(),
'wp-block-serialization-spec-parser' => array(),
+ 'wp-block-editor' => array(
+ 'lodash',
+ 'wp-blocks',
+ 'wp-compose',
+ 'wp-components',
+ 'wp-data',
+ 'wp-element',
+ 'wp-i18n',
+ ),
'wp-blocks' => array(
'lodash',
'wp-autop',
@@ -138,6 +147,7 @@
'wp-a11y',
'wp-api-fetch',
'wp-blob',
+ 'wp-block-editor',
'wp-blocks',
'wp-components',
'wp-compose',
diff --git a/package-lock.json b/package-lock.json
index 656ba9ae3cd0e..a5737be8fefbc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2531,6 +2531,21 @@
"@babel/runtime": "^7.3.1"
}
},
+ "@wordpress/block-editor": {
+ "version": "file:packages/block-editor",
+ "requires": {
+ "@babel/runtime": "^7.0.0",
+ "@wordpress/blocks": "file:packages/blocks",
+ "@wordpress/components": "file:packages/components",
+ "@wordpress/compose": "file:packages/compose",
+ "@wordpress/data": "file:packages/data",
+ "@wordpress/element": "file:packages/element",
+ "@wordpress/i18n": "file:packages/i18n",
+ "lodash": "^4.17.10",
+ "refx": "^3.0.0",
+ "rememo": "^3.0.0"
+ }
+ },
"@wordpress/block-library": {
"version": "file:packages/block-library",
"requires": {
@@ -2763,6 +2778,7 @@
"@wordpress/a11y": "file:packages/a11y",
"@wordpress/api-fetch": "file:packages/api-fetch",
"@wordpress/blob": "file:packages/blob",
+ "@wordpress/block-editor": "file:packages/block-editor",
"@wordpress/blocks": "file:packages/blocks",
"@wordpress/components": "file:packages/components",
"@wordpress/compose": "file:packages/compose",
diff --git a/package.json b/package.json
index 2299994e4f385..88a28a598a7f7 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@wordpress/api-fetch": "file:packages/api-fetch",
"@wordpress/autop": "file:packages/autop",
"@wordpress/blob": "file:packages/blob",
+ "@wordpress/block-editor": "file:packages/block-editor",
"@wordpress/block-library": "file:packages/block-library",
"@wordpress/block-serialization-default-parser": "file:packages/block-serialization-default-parser",
"@wordpress/block-serialization-spec-parser": "file:packages/block-serialization-spec-parser",
diff --git a/packages/block-editor/.npmrc b/packages/block-editor/.npmrc
new file mode 100644
index 0000000000000..43c97e719a5a8
--- /dev/null
+++ b/packages/block-editor/.npmrc
@@ -0,0 +1 @@
+package-lock=false
diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md
new file mode 100644
index 0000000000000..b5527e39e4e7c
--- /dev/null
+++ b/packages/block-editor/CHANGELOG.md
@@ -0,0 +1,5 @@
+## 1.0.0 (Unreleased)
+
+### New Features
+
+- Initial version.
diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md
new file mode 100644
index 0000000000000..9e07cf10eb2ce
--- /dev/null
+++ b/packages/block-editor/README.md
@@ -0,0 +1,13 @@
+# Block Editor
+
+Generic Block Editor Module.
+
+## Installation
+
+Install the module
+
+```bash
+npm install @wordpress/block-editor --save
+```
+
+_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. Learn more about it in [Babel docs](https://babeljs.io/docs/en/next/caveats)._
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
new file mode 100644
index 0000000000000..d4f3cf7f757c7
--- /dev/null
+++ b/packages/block-editor/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "@wordpress/block-editor",
+ "version": "1.0.0-alpha.0",
+ "description": "Generic Block Editor.",
+ "author": "The WordPress Contributors",
+ "license": "GPL-2.0-or-later",
+ "keywords": [
+ "wordpress",
+ "editor",
+ "blocks"
+ ],
+ "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/block-editor/README.md",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/WordPress/gutenberg.git"
+ },
+ "bugs": {
+ "url": "https://github.com/WordPress/gutenberg/issues"
+ },
+ "main": "build/index.js",
+ "module": "build-module/index.js",
+ "react-native": "src/index",
+ "dependencies": {
+ "@babel/runtime": "^7.0.0",
+ "@wordpress/blocks": "file:../blocks",
+ "@wordpress/components": "file:../components",
+ "@wordpress/compose": "file:../compose",
+ "@wordpress/data": "file:../data",
+ "@wordpress/element": "file:../element",
+ "@wordpress/i18n": "file:../i18n",
+ "lodash": "^4.17.10",
+ "refx": "^3.0.0",
+ "rememo": "^3.0.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js
new file mode 100644
index 0000000000000..cb2cca8f110ba
--- /dev/null
+++ b/packages/block-editor/src/components/index.js
@@ -0,0 +1 @@
+export { default as BlockEditorProvider } from './provider';
diff --git a/packages/block-editor/src/components/provider/index.js b/packages/block-editor/src/components/provider/index.js
new file mode 100644
index 0000000000000..a1d3063962b5d
--- /dev/null
+++ b/packages/block-editor/src/components/provider/index.js
@@ -0,0 +1,152 @@
+/**
+ * WordPress dependencies
+ */
+import { Component } from '@wordpress/element';
+import { DropZoneProvider, SlotFillProvider } from '@wordpress/components';
+import { withDispatch, RegistryConsumer } from '@wordpress/data';
+import { createHigherOrderComponent, compose } from '@wordpress/compose';
+
+/**
+ * Higher-order component which renders the original component with the current
+ * registry context passed as its `registry` prop.
+ *
+ * @param {WPComponent} OriginalComponent Original component.
+ *
+ * @return {WPComponent} Enhanced component.
+ */
+const withRegistry = createHigherOrderComponent(
+ ( OriginalComponent ) => ( props ) => (
+
+ { ( registry ) => (
+
+ ) }
+
+ ),
+ 'withRegistry'
+);
+
+class BlockEditorProvider extends Component {
+ componentDidMount() {
+ this.props.updateEditorSettings( this.props.settings );
+ this.props.resetBlocks( this.props.value );
+ this.attachChangeObserver( this.props.registry );
+ }
+
+ componentDidUpdate( prevProps ) {
+ const {
+ settings,
+ updateEditorSettings,
+ value,
+ resetBlocks,
+ registry,
+ } = this.props;
+
+ if ( settings !== prevProps.settings ) {
+ updateEditorSettings( settings );
+ }
+
+ if ( registry !== prevProps.registry ) {
+ this.attachChangeObserver( registry );
+ }
+
+ if ( this.isSyncingOutcomingValue ) {
+ this.isSyncingOutcomingValue = false;
+ } else if ( value !== prevProps.value ) {
+ this.isSyncingIncomingValue = true;
+ resetBlocks( value );
+ }
+ }
+
+ componentWillUnmount() {
+ if ( this.unsubscribe ) {
+ this.unsubscribe();
+ }
+ }
+
+ /**
+ * Given a registry object, overrides the default dispatch behavior for the
+ * `core/block-editor` store to interpret a state change and decide whether
+ * we should call `onChange` or `onInput` depending on whether the change
+ * is persistent or not.
+ *
+ * This needs to be done synchronously after state changes (instead of using
+ * `componentDidUpdate`) in order to avoid batching these changes.
+ *
+ * @param {WPDataRegistry} registry Registry from which block editor
+ * dispatch is to be overriden.
+ */
+ attachChangeObserver( registry ) {
+ if ( this.unsubscribe ) {
+ this.unsubscribe();
+ }
+
+ const {
+ getBlocks,
+ isLastBlockChangePersistent,
+ } = registry.select( 'core/block-editor' );
+
+ let blocks = getBlocks();
+ let isPersistent = isLastBlockChangePersistent();
+
+ this.unsubscribe = registry.subscribe( () => {
+ const {
+ onChange,
+ onInput,
+ } = this.props;
+ const newBlocks = getBlocks();
+ const newIsPersistent = isLastBlockChangePersistent();
+ if ( newBlocks !== blocks && this.isSyncingIncomingValue ) {
+ this.isSyncingIncomingValue = false;
+ blocks = newBlocks;
+ isPersistent = newIsPersistent;
+ return;
+ }
+
+ if (
+ newBlocks !== blocks ||
+ // This happens when a previous input is explicitely marked as persistent.
+ ( newIsPersistent && ! isPersistent )
+ ) {
+ blocks = newBlocks;
+ isPersistent = newIsPersistent;
+
+ this.isSyncingOutcomingValue = true;
+ if ( isPersistent ) {
+ onChange( blocks );
+ } else {
+ onInput( blocks );
+ }
+ }
+ } );
+ }
+
+ render() {
+ const { children } = this.props;
+
+ return (
+
+
+ { children }
+
+
+ );
+ }
+}
+
+export default compose( [
+ withDispatch( ( dispatch ) => {
+ const {
+ updateEditorSettings,
+ resetBlocks,
+ } = dispatch( 'core/block-editor' );
+
+ return {
+ updateEditorSettings,
+ resetBlocks,
+ };
+ } ),
+ withRegistry,
+] )( BlockEditorProvider );
diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js
new file mode 100644
index 0000000000000..9421db61f16e9
--- /dev/null
+++ b/packages/block-editor/src/index.js
@@ -0,0 +1,11 @@
+/**
+ * WordPress dependencies
+ */
+import '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import './store';
+
+export * from './components';
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
new file mode 100644
index 0000000000000..382a25f438d34
--- /dev/null
+++ b/packages/block-editor/src/store/actions.js
@@ -0,0 +1,544 @@
+/**
+ * External dependencies
+ */
+import { castArray } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { getDefaultBlockName, createBlock } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import { select } from './controls';
+
+/**
+ * Returns an action object used in signalling that blocks state should be
+ * reset to the specified array of blocks, taking precedence over any other
+ * content reflected as an edit in state.
+ *
+ * @param {Array} blocks Array of blocks.
+ *
+ * @return {Object} Action object.
+ */
+export function resetBlocks( blocks ) {
+ return {
+ type: 'RESET_BLOCKS',
+ blocks,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that blocks have been received.
+ * Unlike resetBlocks, these should be appended to the existing known set, not
+ * replacing.
+ *
+ * @param {Object[]} blocks Array of block objects.
+ *
+ * @return {Object} Action object.
+ */
+export function receiveBlocks( blocks ) {
+ return {
+ type: 'RECEIVE_BLOCKS',
+ blocks,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the block attributes with
+ * the specified client ID has been updated.
+ *
+ * @param {string} clientId Block client ID.
+ * @param {Object} attributes Block attributes to be merged.
+ *
+ * @return {Object} Action object.
+ */
+export function updateBlockAttributes( clientId, attributes ) {
+ return {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId,
+ attributes,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the block with the
+ * specified client ID has been updated.
+ *
+ * @param {string} clientId Block client ID.
+ * @param {Object} updates Block attributes to be merged.
+ *
+ * @return {Object} Action object.
+ */
+export function updateBlock( clientId, updates ) {
+ return {
+ type: 'UPDATE_BLOCK',
+ clientId,
+ updates,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the block with the
+ * specified client ID has been selected, optionally accepting a position
+ * value reflecting its selection directionality. An initialPosition of -1
+ * reflects a reverse selection.
+ *
+ * @param {string} clientId Block client ID.
+ * @param {?number} initialPosition Optional initial position. Pass as -1 to
+ * reflect reverse selection.
+ *
+ * @return {Object} Action object.
+ */
+export function selectBlock( clientId, initialPosition = null ) {
+ return {
+ type: 'SELECT_BLOCK',
+ initialPosition,
+ clientId,
+ };
+}
+
+/**
+ * Yields action objects used in signalling that the block preceding the given
+ * clientId should be selected.
+ *
+ * @param {string} clientId Block client ID.
+ */
+export function* selectPreviousBlock( clientId ) {
+ const previousBlockClientId = yield select(
+ 'core/editor',
+ 'getPreviousBlockClientId',
+ clientId
+ );
+
+ yield selectBlock( previousBlockClientId, -1 );
+}
+
+/**
+ * Yields action objects used in signalling that the block following the given
+ * clientId should be selected.
+ *
+ * @param {string} clientId Block client ID.
+ */
+export function* selectNextBlock( clientId ) {
+ const nextBlockClientId = yield select(
+ 'core/editor',
+ 'getNextBlockClientId',
+ clientId
+ );
+
+ yield selectBlock( nextBlockClientId );
+}
+
+/**
+ * Returns an action object used in signalling that a block multi-selection has started.
+ *
+ * @return {Object} Action object.
+ */
+export function startMultiSelect() {
+ return {
+ type: 'START_MULTI_SELECT',
+ };
+}
+
+/**
+ * Returns an action object used in signalling that block multi-selection stopped.
+ *
+ * @return {Object} Action object.
+ */
+export function stopMultiSelect() {
+ return {
+ type: 'STOP_MULTI_SELECT',
+ };
+}
+
+/**
+ * Returns an action object used in signalling that block multi-selection changed.
+ *
+ * @param {string} start First block of the multi selection.
+ * @param {string} end Last block of the multiselection.
+ *
+ * @return {Object} Action object.
+ */
+export function multiSelect( start, end ) {
+ return {
+ type: 'MULTI_SELECT',
+ start,
+ end,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the block selection is cleared.
+ *
+ * @return {Object} Action object.
+ */
+export function clearSelectedBlock() {
+ return {
+ type: 'CLEAR_SELECTED_BLOCK',
+ };
+}
+
+/**
+ * Returns an action object that enables or disables block selection.
+ *
+ * @param {boolean} [isSelectionEnabled=true] Whether block selection should
+ * be enabled.
+
+ * @return {Object} Action object.
+ */
+export function toggleSelection( isSelectionEnabled = true ) {
+ return {
+ type: 'TOGGLE_SELECTION',
+ isSelectionEnabled,
+ };
+}
+
+/**
+ * Returns an action object signalling that a blocks should be replaced with
+ * one or more replacement blocks.
+ *
+ * @param {(string|string[])} clientIds Block client ID(s) to replace.
+ * @param {(Object|Object[])} blocks Replacement block(s).
+ *
+ * @return {Object} Action object.
+ */
+export function replaceBlocks( clientIds, blocks ) {
+ return {
+ type: 'REPLACE_BLOCKS',
+ clientIds: castArray( clientIds ),
+ blocks: castArray( blocks ),
+ time: Date.now(),
+ };
+}
+
+/**
+ * Returns an action object signalling that a single block should be replaced
+ * with one or more replacement blocks.
+ *
+ * @param {(string|string[])} clientId Block client ID to replace.
+ * @param {(Object|Object[])} block Replacement block(s).
+ *
+ * @return {Object} Action object.
+ */
+export function replaceBlock( clientId, block ) {
+ return replaceBlocks( clientId, block );
+}
+
+/**
+ * Higher-order action creator which, given the action type to dispatch creates
+ * an action creator for managing block movement.
+ *
+ * @param {string} type Action type to dispatch.
+ *
+ * @return {Function} Action creator.
+ */
+function createOnMove( type ) {
+ return ( clientIds, rootClientId ) => {
+ return {
+ clientIds: castArray( clientIds ),
+ type,
+ rootClientId,
+ };
+ };
+}
+
+export const moveBlocksDown = createOnMove( 'MOVE_BLOCKS_DOWN' );
+export const moveBlocksUp = createOnMove( 'MOVE_BLOCKS_UP' );
+
+/**
+ * Returns an action object signalling that an indexed block should be moved
+ * to a new index.
+ *
+ * @param {?string} clientId The client ID of the block.
+ * @param {?string} fromRootClientId Root client ID source.
+ * @param {?string} toRootClientId Root client ID destination.
+ * @param {number} index The index to move the block into.
+ *
+ * @return {Object} Action object.
+ */
+export function moveBlockToPosition( clientId, fromRootClientId, toRootClientId, index ) {
+ return {
+ type: 'MOVE_BLOCK_TO_POSITION',
+ fromRootClientId,
+ toRootClientId,
+ clientId,
+ index,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that a single block should be
+ * inserted, optionally at a specific index respective a root block list.
+ *
+ * @param {Object} block Block object to insert.
+ * @param {?number} index Index at which block should be inserted.
+ * @param {?string} rootClientId Optional root client ID of block list on which to insert.
+ * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
+ *
+ * @return {Object} Action object.
+ */
+export function insertBlock( block, index, rootClientId, updateSelection = true ) {
+ return insertBlocks( [ block ], index, rootClientId, updateSelection );
+}
+
+/**
+ * Returns an action object used in signalling that an array of blocks should
+ * be inserted, optionally at a specific index respective a root block list.
+ *
+ * @param {Object[]} blocks Block objects to insert.
+ * @param {?number} index Index at which block should be inserted.
+ * @param {?string} rootClientId Optional root client ID of block list on which to insert.
+ * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
+ *
+ * @return {Object} Action object.
+ */
+export function insertBlocks( blocks, index, rootClientId, updateSelection = true ) {
+ return {
+ type: 'INSERT_BLOCKS',
+ blocks: castArray( blocks ),
+ index,
+ rootClientId,
+ time: Date.now(),
+ updateSelection,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the insertion point should
+ * be shown.
+ *
+ * @param {?string} rootClientId Optional root client ID of block list on
+ * which to insert.
+ * @param {?number} index Index at which block should be inserted.
+ *
+ * @return {Object} Action object.
+ */
+export function showInsertionPoint( rootClientId, index ) {
+ return {
+ type: 'SHOW_INSERTION_POINT',
+ rootClientId,
+ index,
+ };
+}
+
+/**
+ * Returns an action object hiding the insertion point.
+ *
+ * @return {Object} Action object.
+ */
+export function hideInsertionPoint() {
+ return {
+ type: 'HIDE_INSERTION_POINT',
+ };
+}
+
+/**
+ * Returns an action object resetting the template validity.
+ *
+ * @param {boolean} isValid template validity flag.
+ *
+ * @return {Object} Action object.
+ */
+export function setTemplateValidity( isValid ) {
+ return {
+ type: 'SET_TEMPLATE_VALIDITY',
+ isValid,
+ };
+}
+
+/**
+ * Returns an action object synchronize the template with the list of blocks
+ *
+ * @return {Object} Action object.
+ */
+export function synchronizeTemplate() {
+ return {
+ type: 'SYNCHRONIZE_TEMPLATE',
+ };
+}
+
+/**
+ * Returns an action object used in signalling that two blocks should be merged
+ *
+ * @param {string} firstBlockClientId Client ID of the first block to merge.
+ * @param {string} secondBlockClientId Client ID of the second block to merge.
+ *
+ * @return {Object} Action object.
+ */
+export function mergeBlocks( firstBlockClientId, secondBlockClientId ) {
+ return {
+ type: 'MERGE_BLOCKS',
+ blocks: [ firstBlockClientId, secondBlockClientId ],
+ };
+}
+
+/**
+ * Yields action objects used in signalling that the blocks corresponding to
+ * the set of specified client IDs are to be removed.
+ *
+ * @param {string|string[]} clientIds Client IDs of blocks to remove.
+ * @param {boolean} selectPrevious True if the previous block should be
+ * selected when a block is removed.
+ */
+export function* removeBlocks( clientIds, selectPrevious = true ) {
+ clientIds = castArray( clientIds );
+
+ if ( selectPrevious ) {
+ yield selectPreviousBlock( clientIds[ 0 ] );
+ }
+
+ yield {
+ type: 'REMOVE_BLOCKS',
+ clientIds,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the block with the
+ * specified client ID is to be removed.
+ *
+ * @param {string} clientId Client ID of block to remove.
+ * @param {boolean} selectPrevious True if the previous block should be
+ * selected when a block is removed.
+ *
+ * @return {Object} Action object.
+ */
+export function removeBlock( clientId, selectPrevious ) {
+ return removeBlocks( [ clientId ], selectPrevious );
+}
+
+/**
+ * Returns an action object used to toggle the block editing mode between
+ * visual and HTML modes.
+ *
+ * @param {string} clientId Block client ID.
+ *
+ * @return {Object} Action object.
+ */
+export function toggleBlockMode( clientId ) {
+ return {
+ type: 'TOGGLE_BLOCK_MODE',
+ clientId,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the user has begun to type.
+ *
+ * @return {Object} Action object.
+ */
+export function startTyping() {
+ return {
+ type: 'START_TYPING',
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the user has stopped typing.
+ *
+ * @return {Object} Action object.
+ */
+export function stopTyping() {
+ return {
+ type: 'STOP_TYPING',
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the caret has entered formatted text.
+ *
+ * @return {Object} Action object.
+ */
+export function enterFormattedText() {
+ return {
+ type: 'ENTER_FORMATTED_TEXT',
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the user caret has exited formatted text.
+ *
+ * @return {Object} Action object.
+ */
+export function exitFormattedText() {
+ return {
+ type: 'EXIT_FORMATTED_TEXT',
+ };
+}
+
+/**
+ * Returns an action object used in signalling that a new block of the default
+ * type should be added to the block list.
+ *
+ * @param {?Object} attributes Optional attributes of the block to assign.
+ * @param {?string} rootClientId Optional root client ID of block list on which
+ * to append.
+ * @param {?number} index Optional index where to insert the default block
+ *
+ * @return {Object} Action object
+ */
+export function insertDefaultBlock( attributes, rootClientId, index ) {
+ const block = createBlock( getDefaultBlockName(), attributes );
+
+ return insertBlock( block, index, rootClientId );
+}
+
+/**
+ * Returns an action object that changes the nested settings of a given block.
+ *
+ * @param {string} clientId Client ID of the block whose nested setting are
+ * being received.
+ * @param {Object} settings Object with the new settings for the nested block.
+ *
+ * @return {Object} Action object
+ */
+export function updateBlockListSettings( clientId, settings ) {
+ return {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId,
+ settings,
+ };
+}
+
+/*
+ * Returns an action object used in signalling that the editor settings have been updated.
+ *
+ * @param {Object} settings Updated settings
+ *
+ * @return {Object} Action object
+ */
+export function updateEditorSettings( settings ) {
+ return {
+ type: 'UPDATE_EDITOR_SETTINGS',
+ settings,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that a temporary reusable blocks have been saved
+ * in order to switch its temporary id with the real id.
+ *
+ * @param {string} id Reusable block's id.
+ * @param {string} updatedId Updated block's id.
+ *
+ * @return {Object} Action object.
+ */
+export function __unstableSaveReusableBlock( id, updatedId ) {
+ return {
+ type: 'SAVE_REUSABLE_BLOCK_SUCCESS',
+ id,
+ updatedId,
+ };
+}
+
+/**
+ * Returns an action object used in signalling that the last block change should be marked explicitely as persistent.
+ *
+ * @return {Object} Action object.
+ */
+export function __unstableMarkLastChangeAsPersistent() {
+ return { type: 'MARK_LAST_CHANGE_AS_PERSISTENT' };
+}
+
diff --git a/packages/editor/src/store/array.js b/packages/block-editor/src/store/array.js
similarity index 100%
rename from packages/editor/src/store/array.js
rename to packages/block-editor/src/store/array.js
diff --git a/packages/block-editor/src/store/controls.js b/packages/block-editor/src/store/controls.js
new file mode 100644
index 0000000000000..5012ab244c21c
--- /dev/null
+++ b/packages/block-editor/src/store/controls.js
@@ -0,0 +1,30 @@
+/**
+ * WordPress dependencies
+ */
+import { createRegistryControl } from '@wordpress/data';
+
+/**
+ * Calls a selector using the current state.
+ *
+ * @param {string} storeName Store name.
+ * @param {string} selectorName Selector name.
+ * @param {Array} args Selector arguments.
+ *
+ * @return {Object} control descriptor.
+ */
+export function select( storeName, selectorName, ...args ) {
+ return {
+ type: 'SELECT',
+ storeName,
+ selectorName,
+ args,
+ };
+}
+
+const controls = {
+ SELECT: createRegistryControl( ( registry ) => ( { storeName, selectorName, args } ) => {
+ return registry.select( storeName )[ selectorName ]( ...args );
+ } ),
+};
+
+export default controls;
diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js
new file mode 100644
index 0000000000000..31d1574d6283a
--- /dev/null
+++ b/packages/block-editor/src/store/defaults.js
@@ -0,0 +1,133 @@
+/**
+ * WordPress dependencies
+ */
+import { __, _x } from '@wordpress/i18n';
+
+export const PREFERENCES_DEFAULTS = {
+ insertUsage: {},
+};
+
+/**
+ * The default editor settings
+ *
+ * alignWide boolean Enable/Disable Wide/Full Alignments
+ * colors Array Palette colors
+ * fontSizes Array Available font sizes
+ * imageSizes Array Available image sizes
+ * maxWidth number Max width to constraint resizing
+ * blockTypes boolean|Array Allowed block types
+ * hasFixedToolbar boolean Whether or not the editor toolbar is fixed
+ * focusMode boolean Whether the focus mode is enabled or not
+ * richEditingEnabled boolean Whether rich editing is enabled or not
+ */
+export const EDITOR_SETTINGS_DEFAULTS = {
+ alignWide: false,
+ colors: [
+ {
+ name: __( 'Pale pink' ),
+ slug: 'pale-pink',
+ color: '#f78da7',
+ },
+ { name: __( 'Vivid red' ),
+ slug: 'vivid-red',
+ color: '#cf2e2e',
+ },
+ {
+ name: __( 'Luminous vivid orange' ),
+ slug: 'luminous-vivid-orange',
+ color: '#ff6900',
+ },
+ {
+ name: __( 'Luminous vivid amber' ),
+ slug: 'luminous-vivid-amber',
+ color: '#fcb900',
+ },
+ {
+ name: __( 'Light green cyan' ),
+ slug: 'light-green-cyan',
+ color: '#7bdcb5',
+ },
+ {
+ name: __( 'Vivid green cyan' ),
+ slug: 'vivid-green-cyan',
+ color: '#00d084',
+ },
+ {
+ name: __( 'Pale cyan blue' ),
+ slug: 'pale-cyan-blue',
+ color: '#8ed1fc',
+ },
+ {
+ name: __( 'Vivid cyan blue' ),
+ slug: 'vivid-cyan-blue',
+ color: '#0693e3',
+ },
+ {
+ name: __( 'Very light gray' ),
+ slug: 'very-light-gray',
+ color: '#eeeeee',
+ },
+ {
+ name: __( 'Cyan bluish gray' ),
+ slug: 'cyan-bluish-gray',
+ color: '#abb8c3',
+ },
+ {
+ name: __( 'Very dark gray' ),
+ slug: 'very-dark-gray',
+ color: '#313131',
+ },
+ ],
+
+ fontSizes: [
+ {
+ name: _x( 'Small', 'font size name' ),
+ size: 13,
+ slug: 'small',
+ },
+ {
+ name: _x( 'Normal', 'font size name' ),
+ size: 16,
+ slug: 'normal',
+ },
+ {
+ name: _x( 'Medium', 'font size name' ),
+ size: 20,
+ slug: 'medium',
+ },
+ {
+ name: _x( 'Large', 'font size name' ),
+ size: 36,
+ slug: 'large',
+ },
+ {
+ name: _x( 'Huge', 'font size name' ),
+ size: 48,
+ slug: 'huge',
+ },
+ ],
+
+ imageSizes: [
+ { slug: 'thumbnail', label: __( 'Thumbnail' ) },
+ { slug: 'medium', label: __( 'Medium' ) },
+ { slug: 'large', label: __( 'Large' ) },
+ { slug: 'full', label: __( 'Full Size' ) },
+ ],
+
+ // This is current max width of the block inner area
+ // It's used to constraint image resizing and this value could be overridden later by themes
+ maxWidth: 580,
+
+ // Allowed block types for the editor, defaulting to true (all supported).
+ allowedBlockTypes: true,
+
+ // Maximum upload size in bytes allowed for the site.
+ maxUploadFileSize: 0,
+
+ // List of allowed mime types and file extensions.
+ allowedMimeTypes: null,
+
+ // Whether richs editing is enabled or not.
+ richEditingEnabled: true,
+};
+
diff --git a/packages/block-editor/src/store/effects.js b/packages/block-editor/src/store/effects.js
new file mode 100644
index 0000000000000..f02ef6fbd7231
--- /dev/null
+++ b/packages/block-editor/src/store/effects.js
@@ -0,0 +1,150 @@
+/**
+ * WordPress dependencies
+ */
+import { speak } from '@wordpress/a11y';
+import {
+ getBlockType,
+ doBlocksMatchTemplate,
+ switchToBlockType,
+ synchronizeBlocksWithTemplate,
+} from '@wordpress/blocks';
+import { _n, sprintf } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import {
+ replaceBlocks,
+ selectBlock,
+ setTemplateValidity,
+ insertDefaultBlock,
+ resetBlocks,
+} from './actions';
+import {
+ getBlock,
+ getBlocks,
+ getSelectedBlockCount,
+ getBlockCount,
+ getTemplateLock,
+ getTemplate,
+ isValidTemplate,
+} from './selectors';
+
+/**
+ * Block validity is a function of blocks state (at the point of a
+ * reset) and the template setting. As a compromise to its placement
+ * across distinct parts of state, it is implemented here as a side-
+ * effect of the block reset action.
+ *
+ * @param {Object} action RESET_BLOCKS action.
+ * @param {Object} store Store instance.
+ *
+ * @return {?Object} New validity set action if validity has changed.
+ */
+export function validateBlocksToTemplate( action, store ) {
+ const state = store.getState();
+ const template = getTemplate( state );
+ const templateLock = getTemplateLock( state );
+
+ // Unlocked templates are considered always valid because they act
+ // as default values only.
+ const isBlocksValidToTemplate = (
+ ! template ||
+ templateLock !== 'all' ||
+ doBlocksMatchTemplate( action.blocks, template )
+ );
+
+ // Update if validity has changed.
+ if ( isBlocksValidToTemplate !== isValidTemplate( state ) ) {
+ return setTemplateValidity( isBlocksValidToTemplate );
+ }
+}
+
+/**
+ * Effect handler which will return a default block insertion action if there
+ * are no other blocks at the root of the editor. This is expected to be used
+ * in actions which may result in no blocks remaining in the editor (removal,
+ * replacement, etc).
+ *
+ * @param {Object} action Action which had initiated the effect handler.
+ * @param {Object} store Store instance.
+ *
+ * @return {?Object} Default block insert action, if no other blocks exist.
+ */
+export function ensureDefaultBlock( action, store ) {
+ if ( ! getBlockCount( store.getState() ) ) {
+ return insertDefaultBlock();
+ }
+}
+
+export default {
+ MERGE_BLOCKS( action, store ) {
+ const { dispatch } = store;
+ const state = store.getState();
+ const [ firstBlockClientId, secondBlockClientId ] = action.blocks;
+ const blockA = getBlock( state, firstBlockClientId );
+ const blockType = getBlockType( blockA.name );
+
+ // Only focus the previous block if it's not mergeable
+ if ( ! blockType.merge ) {
+ dispatch( selectBlock( blockA.clientId ) );
+ return;
+ }
+
+ // We can only merge blocks with similar types
+ // thus, we transform the block to merge first
+ const blockB = getBlock( state, secondBlockClientId );
+ const blocksWithTheSameType = blockA.name === blockB.name ?
+ [ blockB ] :
+ switchToBlockType( blockB, blockA.name );
+
+ // If the block types can not match, do nothing
+ if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) {
+ return;
+ }
+
+ // Calling the merge to update the attributes and remove the block to be merged
+ const updatedAttributes = blockType.merge(
+ blockA.attributes,
+ blocksWithTheSameType[ 0 ].attributes
+ );
+
+ dispatch( selectBlock( blockA.clientId, -1 ) );
+ dispatch( replaceBlocks(
+ [ blockA.clientId, blockB.clientId ],
+ [
+ {
+ ...blockA,
+ attributes: {
+ ...blockA.attributes,
+ ...updatedAttributes,
+ },
+ },
+ ...blocksWithTheSameType.slice( 1 ),
+ ]
+ ) );
+ },
+ RESET_BLOCKS: [
+ validateBlocksToTemplate,
+ ],
+ REMOVE_BLOCKS: [
+ ensureDefaultBlock,
+ ],
+ REPLACE_BLOCKS: [
+ ensureDefaultBlock,
+ ],
+ MULTI_SELECT: ( action, { getState } ) => {
+ const blockCount = getSelectedBlockCount( getState() );
+
+ /* translators: %s: number of selected blocks */
+ speak( sprintf( _n( '%s block selected.', '%s blocks selected.', blockCount ), blockCount ), 'assertive' );
+ },
+ SYNCHRONIZE_TEMPLATE( action, { getState } ) {
+ const state = getState();
+ const blocks = getBlocks( state );
+ const template = getTemplate( state );
+ const updatedBlockList = synchronizeBlocksWithTemplate( blocks, template );
+
+ return resetBlocks( updatedBlockList );
+ },
+};
diff --git a/packages/block-editor/src/store/index.js b/packages/block-editor/src/store/index.js
new file mode 100644
index 0000000000000..0119e63d7a3d1
--- /dev/null
+++ b/packages/block-editor/src/store/index.js
@@ -0,0 +1,29 @@
+/**
+ * WordPress dependencies
+ */
+import { registerStore } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import reducer from './reducer';
+import applyMiddlewares from './middlewares';
+import * as selectors from './selectors';
+import * as actions from './actions';
+import controls from './controls';
+
+/**
+ * Module Constants
+ */
+const MODULE_KEY = 'core/block-editor';
+
+const store = registerStore( MODULE_KEY, {
+ reducer,
+ selectors,
+ actions,
+ controls,
+ persist: [ 'preferences' ],
+} );
+applyMiddlewares( store );
+
+export default store;
diff --git a/packages/block-editor/src/store/middlewares.js b/packages/block-editor/src/store/middlewares.js
new file mode 100644
index 0000000000000..6381132bb81e0
--- /dev/null
+++ b/packages/block-editor/src/store/middlewares.js
@@ -0,0 +1,45 @@
+/**
+ * External dependencies
+ */
+import refx from 'refx';
+import multi from 'redux-multi';
+import { flowRight } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import effects from './effects';
+
+/**
+ * Applies the custom middlewares used specifically in the editor module.
+ *
+ * @param {Object} store Store Object.
+ *
+ * @return {Object} Update Store Object.
+ */
+function applyMiddlewares( store ) {
+ const middlewares = [
+ refx( effects ),
+ multi,
+ ];
+
+ let enhancedDispatch = () => {
+ throw new Error(
+ 'Dispatching while constructing your middleware is not allowed. ' +
+ 'Other middleware would not be applied to this dispatch.'
+ );
+ };
+ let chain = [];
+
+ const middlewareAPI = {
+ getState: store.getState,
+ dispatch: ( ...args ) => enhancedDispatch( ...args ),
+ };
+ chain = middlewares.map( ( middleware ) => middleware( middlewareAPI ) );
+ enhancedDispatch = flowRight( ...chain )( store.dispatch );
+
+ store.dispatch = enhancedDispatch;
+ return store;
+}
+
+export default applyMiddlewares;
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
new file mode 100644
index 0000000000000..d6cc2444af050
--- /dev/null
+++ b/packages/block-editor/src/store/reducer.js
@@ -0,0 +1,901 @@
+/**
+ * External dependencies
+ */
+import {
+ flow,
+ reduce,
+ first,
+ last,
+ omit,
+ without,
+ mapValues,
+ keys,
+ isEqual,
+ isEmpty,
+} from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { combineReducers } from '@wordpress/data';
+import { isReusableBlock } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import {
+ PREFERENCES_DEFAULTS,
+ EDITOR_SETTINGS_DEFAULTS,
+} from './defaults';
+import { insertAt, moveTo } from './array';
+
+/**
+ * Given an array of blocks, returns an object where each key is a nesting
+ * context, the value of which is an array of block client IDs existing within
+ * that nesting context.
+ *
+ * @param {Array} blocks Blocks to map.
+ * @param {?string} rootClientId Assumed root client ID.
+ *
+ * @return {Object} Block order map object.
+ */
+function mapBlockOrder( blocks, rootClientId = '' ) {
+ const result = { [ rootClientId ]: [] };
+
+ blocks.forEach( ( block ) => {
+ const { clientId, innerBlocks } = block;
+
+ result[ rootClientId ].push( clientId );
+
+ Object.assign( result, mapBlockOrder( innerBlocks, clientId ) );
+ } );
+
+ return result;
+}
+
+/**
+ * Helper method to iterate through all blocks, recursing into inner blocks,
+ * applying a transformation function to each one.
+ * Returns a flattened object with the transformed blocks.
+ *
+ * @param {Array} blocks Blocks to flatten.
+ * @param {Function} transform Transforming function to be applied to each block.
+ *
+ * @return {Object} Flattened object.
+ */
+function flattenBlocks( blocks, transform ) {
+ const result = {};
+
+ const stack = [ ...blocks ];
+ while ( stack.length ) {
+ const { innerBlocks, ...block } = stack.shift();
+ stack.push( ...innerBlocks );
+ result[ block.clientId ] = transform( block );
+ }
+
+ return result;
+}
+
+/**
+ * Given an array of blocks, returns an object containing all blocks, without
+ * attributes, recursing into inner blocks. Keys correspond to the block client
+ * ID, the value of which is the attributes object.
+ *
+ * @param {Array} blocks Blocks to flatten.
+ *
+ * @return {Object} Flattened block attributes object.
+ */
+function getFlattenedBlocksWithoutAttributes( blocks ) {
+ return flattenBlocks( blocks, ( block ) => omit( block, 'attributes' ) );
+}
+
+/**
+ * Given an array of blocks, returns an object containing all block attributes,
+ * recursing into inner blocks. Keys correspond to the block client ID, the
+ * value of which is the attributes object.
+ *
+ * @param {Array} blocks Blocks to flatten.
+ *
+ * @return {Object} Flattened block attributes object.
+ */
+function getFlattenedBlockAttributes( blocks ) {
+ return flattenBlocks( blocks, ( block ) => block.attributes );
+}
+
+/**
+ * Given a block order map object, returns *all* of the block client IDs that are
+ * a descendant of the given root client ID.
+ *
+ * Calling this with `rootClientId` set to `''` results in a list of client IDs
+ * that are in the post. That is, it excludes blocks like fetched reusable
+ * blocks which are stored into state but not visible.
+ *
+ * @param {Object} blocksOrder Object that maps block client IDs to a list of
+ * nested block client IDs.
+ * @param {?string} rootClientId The root client ID to search. Defaults to ''.
+ *
+ * @return {Array} List of descendant client IDs.
+ */
+function getNestedBlockClientIds( blocksOrder, rootClientId = '' ) {
+ return reduce( blocksOrder[ rootClientId ], ( result, clientId ) => [
+ ...result,
+ clientId,
+ ...getNestedBlockClientIds( blocksOrder, clientId ),
+ ], [] );
+}
+
+/**
+ * Returns an object against which it is safe to perform mutating operations,
+ * given the original object and its current working copy.
+ *
+ * @param {Object} original Original object.
+ * @param {Object} working Working object.
+ *
+ * @return {Object} Mutation-safe object.
+ */
+function getMutateSafeObject( original, working ) {
+ if ( original === working ) {
+ return { ...original };
+ }
+
+ return working;
+}
+
+/**
+ * Returns true if the two object arguments have the same keys, or false
+ * otherwise.
+ *
+ * @param {Object} a First object.
+ * @param {Object} b Second object.
+ *
+ * @return {boolean} Whether the two objects have the same keys.
+ */
+export function hasSameKeys( a, b ) {
+ return isEqual( keys( a ), keys( b ) );
+}
+
+/**
+ * Returns true if, given the currently dispatching action and the previously
+ * dispatched action, the two actions are updating the same block attribute, or
+ * false otherwise.
+ *
+ * @param {Object} action Currently dispatching action.
+ * @param {Object} lastAction Previously dispatched action.
+ *
+ * @return {boolean} Whether actions are updating the same block attribute.
+ */
+export function isUpdatingSameBlockAttribute( action, lastAction ) {
+ return (
+ action.type === 'UPDATE_BLOCK_ATTRIBUTES' &&
+ lastAction !== undefined &&
+ lastAction.type === 'UPDATE_BLOCK_ATTRIBUTES' &&
+ action.clientId === lastAction.clientId &&
+ hasSameKeys( action.attributes, lastAction.attributes )
+ );
+}
+
+/**
+ * Higher-order reducer intended to augment the blocks reducer, assigning an
+ * `isPersistentChange` property value corresponding to whether a change in
+ * state can be considered as persistent. All changes are considered persistent
+ * except when updating the same block attribute as in the previous action.
+ *
+ * @param {Function} reducer Original reducer function.
+ *
+ * @return {Function} Enhanced reducer function.
+ */
+function withPersistentBlockChange( reducer ) {
+ let lastAction;
+
+ return ( state, action ) => {
+ let nextState = reducer( state, action );
+ const isExplicitPersistentChange = action.type === 'MARK_LAST_CHANGE_AS_PERSISTENT';
+
+ if ( state !== nextState || isExplicitPersistentChange ) {
+ nextState = {
+ ...nextState,
+ isPersistentChange: (
+ isExplicitPersistentChange ||
+ ! isUpdatingSameBlockAttribute( action, lastAction )
+ ),
+ };
+ }
+
+ lastAction = action;
+
+ return nextState;
+ };
+}
+
+/**
+ * Higher-order reducer targeting the combined blocks reducer, augmenting
+ * block client IDs in remove action to include cascade of inner blocks.
+ *
+ * @param {Function} reducer Original reducer function.
+ *
+ * @return {Function} Enhanced reducer function.
+ */
+const withInnerBlocksRemoveCascade = ( reducer ) => ( state, action ) => {
+ if ( state && action.type === 'REMOVE_BLOCKS' ) {
+ const clientIds = [ ...action.clientIds ];
+
+ // For each removed client ID, include its inner blocks to remove,
+ // recursing into those so long as inner blocks exist.
+ for ( let i = 0; i < clientIds.length; i++ ) {
+ clientIds.push( ...state.order[ clientIds[ i ] ] );
+ }
+
+ action = { ...action, clientIds };
+ }
+
+ return reducer( state, action );
+};
+
+/**
+ * Higher-order reducer which targets the combined blocks reducer and handles
+ * the `RESET_BLOCKS` action. When dispatched, this action will replace all
+ * blocks that exist in the post, leaving blocks that exist only in state (e.g.
+ * reusable blocks) alone.
+ *
+ * @param {Function} reducer Original reducer function.
+ *
+ * @return {Function} Enhanced reducer function.
+ */
+const withBlockReset = ( reducer ) => ( state, action ) => {
+ if ( state && action.type === 'RESET_BLOCKS' ) {
+ const visibleClientIds = getNestedBlockClientIds( state.order );
+ return {
+ ...state,
+ byClientId: {
+ ...omit( state.byClientId, visibleClientIds ),
+ ...getFlattenedBlocksWithoutAttributes( action.blocks ),
+ },
+ attributes: {
+ ...omit( state.attributes, visibleClientIds ),
+ ...getFlattenedBlockAttributes( action.blocks ),
+ },
+ order: {
+ ...omit( state.order, visibleClientIds ),
+ ...mapBlockOrder( action.blocks ),
+ },
+ };
+ }
+
+ return reducer( state, action );
+};
+
+/**
+ * Higher-order reducer which targets the combined blocks reducer and handles
+ * the `SAVE_REUSABLE_BLOCK_SUCCESS` action. This action can't be handled by
+ * regular reducers and needs a higher-order reducer since it needs access to
+ * both `byClientId` and `attributes` simultaneously.
+ *
+ * @param {Function} reducer Original reducer function.
+ *
+ * @return {Function} Enhanced reducer function.
+ */
+const withSaveReusableBlock = ( reducer ) => ( state, action ) => {
+ if ( state && action.type === 'SAVE_REUSABLE_BLOCK_SUCCESS' ) {
+ const { id, updatedId } = action;
+
+ // If a temporary reusable block is saved, we swap the temporary id with the final one
+ if ( id === updatedId ) {
+ return state;
+ }
+
+ state = { ...state };
+
+ state.attributes = mapValues( state.attributes, ( attributes, clientId ) => {
+ const { name } = state.byClientId[ clientId ];
+ if ( name === 'core/block' && attributes.ref === id ) {
+ return {
+ ...attributes,
+ ref: updatedId,
+ };
+ }
+
+ return attributes;
+ } );
+ }
+
+ return reducer( state, action );
+};
+
+/**
+ * Reducer returning the blocks state.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @returns {Object} Updated state.
+ */
+export const blocks = flow(
+ combineReducers,
+ withInnerBlocksRemoveCascade,
+ withBlockReset,
+ withSaveReusableBlock,
+ withPersistentBlockChange,
+)( {
+ byClientId( state = {}, action ) {
+ switch ( action.type ) {
+ case 'RESET_BLOCKS':
+ return getFlattenedBlocksWithoutAttributes( action.blocks );
+
+ case 'RECEIVE_BLOCKS':
+ return {
+ ...state,
+ ...getFlattenedBlocksWithoutAttributes( action.blocks ),
+ };
+
+ case 'UPDATE_BLOCK':
+ // Ignore updates if block isn't known
+ if ( ! state[ action.clientId ] ) {
+ return state;
+ }
+
+ // Do nothing if only attributes change.
+ const changes = omit( action.updates, 'attributes' );
+ if ( isEmpty( changes ) ) {
+ return state;
+ }
+
+ return {
+ ...state,
+ [ action.clientId ]: {
+ ...state[ action.clientId ],
+ ...changes,
+ },
+ };
+
+ case 'INSERT_BLOCKS':
+ return {
+ ...state,
+ ...getFlattenedBlocksWithoutAttributes( action.blocks ),
+ };
+
+ case 'REPLACE_BLOCKS':
+ if ( ! action.blocks ) {
+ return state;
+ }
+
+ return {
+ ...omit( state, action.clientIds ),
+ ...getFlattenedBlocksWithoutAttributes( action.blocks ),
+ };
+
+ case 'REMOVE_BLOCKS':
+ return omit( state, action.clientIds );
+ }
+
+ return state;
+ },
+
+ attributes( state = {}, action ) {
+ switch ( action.type ) {
+ case 'RESET_BLOCKS':
+ return getFlattenedBlockAttributes( action.blocks );
+
+ case 'RECEIVE_BLOCKS':
+ return {
+ ...state,
+ ...getFlattenedBlockAttributes( action.blocks ),
+ };
+
+ case 'UPDATE_BLOCK':
+ // Ignore updates if block isn't known or there are no attribute changes.
+ if ( ! state[ action.clientId ] || ! action.updates.attributes ) {
+ return state;
+ }
+
+ return {
+ ...state,
+ [ action.clientId ]: {
+ ...state[ action.clientId ],
+ ...action.updates.attributes,
+ },
+ };
+
+ case 'UPDATE_BLOCK_ATTRIBUTES':
+ // Ignore updates if block isn't known
+ if ( ! state[ action.clientId ] ) {
+ return state;
+ }
+
+ // Consider as updates only changed values
+ const nextAttributes = reduce( action.attributes, ( result, value, key ) => {
+ if ( value !== result[ key ] ) {
+ result = getMutateSafeObject( state[ action.clientId ], result );
+ result[ key ] = value;
+ }
+
+ return result;
+ }, state[ action.clientId ] );
+
+ // Skip update if nothing has been changed. The reference will
+ // match the original block if `reduce` had no changed values.
+ if ( nextAttributes === state[ action.clientId ] ) {
+ return state;
+ }
+
+ // Otherwise replace attributes in state
+ return {
+ ...state,
+ [ action.clientId ]: nextAttributes,
+ };
+
+ case 'INSERT_BLOCKS':
+ return {
+ ...state,
+ ...getFlattenedBlockAttributes( action.blocks ),
+ };
+
+ case 'REPLACE_BLOCKS':
+ if ( ! action.blocks ) {
+ return state;
+ }
+
+ return {
+ ...omit( state, action.clientIds ),
+ ...getFlattenedBlockAttributes( action.blocks ),
+ };
+
+ case 'REMOVE_BLOCKS':
+ return omit( state, action.clientIds );
+ }
+
+ return state;
+ },
+
+ order( state = {}, action ) {
+ switch ( action.type ) {
+ case 'RESET_BLOCKS':
+ return mapBlockOrder( action.blocks );
+
+ case 'RECEIVE_BLOCKS':
+ return {
+ ...state,
+ ...omit( mapBlockOrder( action.blocks ), '' ),
+ };
+
+ case 'INSERT_BLOCKS': {
+ const { rootClientId = '' } = action;
+ const subState = state[ rootClientId ] || [];
+ const mappedBlocks = mapBlockOrder( action.blocks, rootClientId );
+ const { index = subState.length } = action;
+
+ return {
+ ...state,
+ ...mappedBlocks,
+ [ rootClientId ]: insertAt( subState, mappedBlocks[ rootClientId ], index ),
+ };
+ }
+
+ case 'MOVE_BLOCK_TO_POSITION': {
+ const { fromRootClientId = '', toRootClientId = '', clientId } = action;
+ const { index = state[ toRootClientId ].length } = action;
+
+ // Moving inside the same parent block
+ if ( fromRootClientId === toRootClientId ) {
+ const subState = state[ toRootClientId ];
+ const fromIndex = subState.indexOf( clientId );
+ return {
+ ...state,
+ [ toRootClientId ]: moveTo( state[ toRootClientId ], fromIndex, index ),
+ };
+ }
+
+ // Moving from a parent block to another
+ return {
+ ...state,
+ [ fromRootClientId ]: without( state[ fromRootClientId ], clientId ),
+ [ toRootClientId ]: insertAt( state[ toRootClientId ], clientId, index ),
+ };
+ }
+
+ case 'MOVE_BLOCKS_UP': {
+ const { clientIds, rootClientId = '' } = action;
+ const firstClientId = first( clientIds );
+ const subState = state[ rootClientId ];
+
+ if ( ! subState.length || firstClientId === first( subState ) ) {
+ return state;
+ }
+
+ const firstIndex = subState.indexOf( firstClientId );
+
+ return {
+ ...state,
+ [ rootClientId ]: moveTo( subState, firstIndex, firstIndex - 1, clientIds.length ),
+ };
+ }
+
+ case 'MOVE_BLOCKS_DOWN': {
+ const { clientIds, rootClientId = '' } = action;
+ const firstClientId = first( clientIds );
+ const lastClientId = last( clientIds );
+ const subState = state[ rootClientId ];
+
+ if ( ! subState.length || lastClientId === last( subState ) ) {
+ return state;
+ }
+
+ const firstIndex = subState.indexOf( firstClientId );
+
+ return {
+ ...state,
+ [ rootClientId ]: moveTo( subState, firstIndex, firstIndex + 1, clientIds.length ),
+ };
+ }
+
+ case 'REPLACE_BLOCKS': {
+ const { clientIds } = action;
+ if ( ! action.blocks ) {
+ return state;
+ }
+
+ const mappedBlocks = mapBlockOrder( action.blocks );
+
+ return flow( [
+ ( nextState ) => omit( nextState, clientIds ),
+ ( nextState ) => ( {
+ ...nextState,
+ ...omit( mappedBlocks, '' ),
+ } ),
+ ( nextState ) => mapValues( nextState, ( subState ) => (
+ reduce( subState, ( result, clientId ) => {
+ if ( clientId === clientIds[ 0 ] ) {
+ return [
+ ...result,
+ ...mappedBlocks[ '' ],
+ ];
+ }
+
+ if ( clientIds.indexOf( clientId ) === -1 ) {
+ result.push( clientId );
+ }
+
+ return result;
+ }, [] )
+ ) ),
+ ] )( state );
+ }
+
+ case 'REMOVE_BLOCKS':
+ return flow( [
+ // Remove inner block ordering for removed blocks
+ ( nextState ) => omit( nextState, action.clientIds ),
+
+ // Remove deleted blocks from other blocks' orderings
+ ( nextState ) => mapValues( nextState, ( subState ) => (
+ without( subState, ...action.clientIds )
+ ) ),
+ ] )( state );
+ }
+
+ return state;
+ },
+} );
+
+/**
+ * Reducer returning typing state.
+ *
+ * @param {boolean} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {boolean} Updated state.
+ */
+export function isTyping( state = false, action ) {
+ switch ( action.type ) {
+ case 'START_TYPING':
+ return true;
+
+ case 'STOP_TYPING':
+ return false;
+ }
+
+ return state;
+}
+
+/**
+ * Reducer returning whether the caret is within formatted text.
+ *
+ * @param {boolean} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {boolean} Updated state.
+ */
+export function isCaretWithinFormattedText( state = false, action ) {
+ switch ( action.type ) {
+ case 'ENTER_FORMATTED_TEXT':
+ return true;
+
+ case 'EXIT_FORMATTED_TEXT':
+ return false;
+ }
+
+ return state;
+}
+
+/**
+ * Reducer returning the block selection's state.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {Object} Updated state.
+ */
+export function blockSelection( state = {
+ start: null,
+ end: null,
+ isMultiSelecting: false,
+ isEnabled: true,
+ initialPosition: null,
+}, action ) {
+ switch ( action.type ) {
+ case 'CLEAR_SELECTED_BLOCK':
+ if ( state.start === null && state.end === null && ! state.isMultiSelecting ) {
+ return state;
+ }
+
+ return {
+ ...state,
+ start: null,
+ end: null,
+ isMultiSelecting: false,
+ initialPosition: null,
+ };
+ case 'START_MULTI_SELECT':
+ if ( state.isMultiSelecting ) {
+ return state;
+ }
+
+ return {
+ ...state,
+ isMultiSelecting: true,
+ initialPosition: null,
+ };
+ case 'STOP_MULTI_SELECT':
+ if ( ! state.isMultiSelecting ) {
+ return state;
+ }
+
+ return {
+ ...state,
+ isMultiSelecting: false,
+ initialPosition: null,
+ };
+ case 'MULTI_SELECT':
+ return {
+ ...state,
+ start: action.start,
+ end: action.end,
+ initialPosition: null,
+ };
+ case 'SELECT_BLOCK':
+ if ( action.clientId === state.start && action.clientId === state.end ) {
+ return state;
+ }
+ return {
+ ...state,
+ start: action.clientId,
+ end: action.clientId,
+ initialPosition: action.initialPosition,
+ };
+ case 'INSERT_BLOCKS': {
+ if ( action.updateSelection ) {
+ return {
+ ...state,
+ start: action.blocks[ 0 ].clientId,
+ end: action.blocks[ 0 ].clientId,
+ initialPosition: null,
+ isMultiSelecting: false,
+ };
+ }
+ return state;
+ }
+ case 'REMOVE_BLOCKS':
+ if ( ! action.clientIds || ! action.clientIds.length || action.clientIds.indexOf( state.start ) === -1 ) {
+ return state;
+ }
+ return {
+ ...state,
+ start: null,
+ end: null,
+ initialPosition: null,
+ isMultiSelecting: false,
+ };
+ case 'REPLACE_BLOCKS':
+ if ( action.clientIds.indexOf( state.start ) === -1 ) {
+ return state;
+ }
+
+ // If there are replacement blocks, assign last block as the next
+ // selected block, otherwise set to null.
+ const lastBlock = last( action.blocks );
+ const nextSelectedBlockClientId = lastBlock ? lastBlock.clientId : null;
+
+ if ( nextSelectedBlockClientId === state.start && nextSelectedBlockClientId === state.end ) {
+ return state;
+ }
+
+ return {
+ ...state,
+ start: nextSelectedBlockClientId,
+ end: nextSelectedBlockClientId,
+ initialPosition: null,
+ isMultiSelecting: false,
+ };
+ case 'TOGGLE_SELECTION':
+ return {
+ ...state,
+ isEnabled: action.isSelectionEnabled,
+ };
+ }
+
+ return state;
+}
+
+export function blocksMode( state = {}, action ) {
+ if ( action.type === 'TOGGLE_BLOCK_MODE' ) {
+ const { clientId } = action;
+ return {
+ ...state,
+ [ clientId ]: state[ clientId ] && state[ clientId ] === 'html' ? 'visual' : 'html',
+ };
+ }
+
+ return state;
+}
+
+/**
+ * Reducer returning the block insertion point visibility, either null if there
+ * is not an explicit insertion point assigned, or an object of its `index` and
+ * `rootClientId`.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {Object} Updated state.
+ */
+export function insertionPoint( state = null, action ) {
+ switch ( action.type ) {
+ case 'SHOW_INSERTION_POINT':
+ const { rootClientId, index } = action;
+ return { rootClientId, index };
+
+ case 'HIDE_INSERTION_POINT':
+ return null;
+ }
+
+ return state;
+}
+
+/**
+ * Reducer returning whether the post blocks match the defined template or not.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {boolean} Updated state.
+ */
+export function template( state = { isValid: true }, action ) {
+ switch ( action.type ) {
+ case 'SET_TEMPLATE_VALIDITY':
+ return {
+ ...state,
+ isValid: action.isValid,
+ };
+ }
+
+ return state;
+}
+
+/**
+ * Reducer returning the editor setting.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {Object} Updated state.
+ */
+export function settings( state = EDITOR_SETTINGS_DEFAULTS, action ) {
+ switch ( action.type ) {
+ case 'UPDATE_EDITOR_SETTINGS':
+ return {
+ ...state,
+ ...action.settings,
+ };
+ }
+
+ return state;
+}
+
+/**
+ * Reducer returning the user preferences.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {string} Updated state.
+ */
+export function preferences( state = PREFERENCES_DEFAULTS, action ) {
+ switch ( action.type ) {
+ case 'INSERT_BLOCKS':
+ case 'REPLACE_BLOCKS':
+ return action.blocks.reduce( ( prevState, block ) => {
+ let id = block.name;
+ const insert = { name: block.name };
+ if ( isReusableBlock( block ) ) {
+ insert.ref = block.attributes.ref;
+ id += '/' + block.attributes.ref;
+ }
+
+ return {
+ ...prevState,
+ insertUsage: {
+ ...prevState.insertUsage,
+ [ id ]: {
+ time: action.time,
+ count: prevState.insertUsage[ id ] ? prevState.insertUsage[ id ].count + 1 : 1,
+ insert,
+ },
+ },
+ };
+ }, state );
+ }
+
+ return state;
+}
+
+/**
+ * Reducer returning an object where each key is a block client ID, its value
+ * representing the settings for its nested blocks.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {Object} Updated state.
+ */
+export const blockListSettings = ( state = {}, action ) => {
+ switch ( action.type ) {
+ // Even if the replaced blocks have the same client ID, our logic
+ // should correct the state.
+ case 'REPLACE_BLOCKS' :
+ case 'REMOVE_BLOCKS': {
+ return omit( state, action.clientIds );
+ }
+ case 'UPDATE_BLOCK_LIST_SETTINGS': {
+ const { clientId } = action;
+ if ( ! action.settings ) {
+ if ( state.hasOwnProperty( clientId ) ) {
+ return omit( state, clientId );
+ }
+
+ return state;
+ }
+
+ if ( isEqual( state[ clientId ], action.settings ) ) {
+ return state;
+ }
+
+ return {
+ ...state,
+ [ clientId ]: action.settings,
+ };
+ }
+ }
+ return state;
+};
+
+export default combineReducers( {
+ blocks,
+ isTyping,
+ isCaretWithinFormattedText,
+ blockSelection,
+ blocksMode,
+ blockListSettings,
+ insertionPoint,
+ template,
+ settings,
+ preferences,
+} );
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
new file mode 100644
index 0000000000000..efadb31bf0ff2
--- /dev/null
+++ b/packages/block-editor/src/store/selectors.js
@@ -0,0 +1,1395 @@
+/**
+ * External dependencies
+ */
+import {
+ castArray,
+ flatMap,
+ first,
+ get,
+ includes,
+ isArray,
+ isBoolean,
+ last,
+ map,
+ orderBy,
+ reduce,
+ some,
+} from 'lodash';
+import createSelector from 'rememo';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ getBlockType,
+ getBlockTypes,
+ hasBlockSupport,
+ hasChildBlocksWithInserterSupport,
+} from '@wordpress/blocks';
+
+/***
+ * Module constants
+ */
+export const INSERTER_UTILITY_HIGH = 3;
+export const INSERTER_UTILITY_MEDIUM = 2;
+export const INSERTER_UTILITY_LOW = 1;
+export const INSERTER_UTILITY_NONE = 0;
+const MILLISECONDS_PER_HOUR = 3600 * 1000;
+const MILLISECONDS_PER_DAY = 24 * 3600 * 1000;
+const MILLISECONDS_PER_WEEK = 7 * 24 * 3600 * 1000;
+
+/**
+ * Shared reference to an empty array for cases where it is important to avoid
+ * returning a new array reference on every invocation, as in a connected or
+ * other pure component which performs `shouldComponentUpdate` check on props.
+ * This should be used as a last resort, since the normalized data should be
+ * maintained by the reducer result in state.
+ *
+ * @type {Array}
+ */
+const EMPTY_ARRAY = [];
+
+/**
+ * Shared reference to an empty object for cases where it is important to avoid
+ * returning a new object reference on every invocation.
+ *
+ * @type {Object}
+ */
+const EMPTY_OBJECT = {};
+
+/**
+ * Returns a new reference when the inner blocks of a given block client ID
+ * change. This is used exclusively as a memoized selector dependant, relying
+ * on this selector's shared return value and recursively those of its inner
+ * blocks defined as dependencies. This abuses mechanics of the selector
+ * memoization to return from the original selector function only when
+ * dependants change.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {*} A value whose reference will change only when inner blocks of
+ * the given block client ID change.
+ */
+export const getBlockDependantsCacheBust = createSelector(
+ () => [],
+ ( state, clientId ) => map(
+ getBlockOrder( state, clientId ),
+ ( innerBlockClientId ) => getBlock( state, innerBlockClientId ),
+ ),
+);
+
+/**
+ * Returns a block's name given its client ID, or null if no block exists with
+ * the client ID.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {string} Block name.
+ */
+export function getBlockName( state, clientId ) {
+ const block = state.blocks.byClientId[ clientId ];
+ return block ? block.name : null;
+}
+
+/**
+ * Returns whether a block is valid or not.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {boolean} Is Valid.
+ */
+export function isBlockValid( state, clientId ) {
+ const block = state.blocks.byClientId[ clientId ];
+ return !! block && block.isValid;
+}
+
+/**
+ * Returns a block's attributes given its client ID, or null if no block exists with
+ * the client ID.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {Object?} Block attributes.
+ */
+export const getBlockAttributes = createSelector(
+ ( state, clientId ) => {
+ const block = state.blocks.byClientId[ clientId ];
+ if ( ! block ) {
+ return null;
+ }
+
+ let attributes = state.blocks.attributes[ clientId ];
+
+ // Inject custom source attribute values.
+ //
+ // TODO: Create generic external sourcing pattern, not explicitly
+ // targeting meta attributes.
+ const type = getBlockType( block.name );
+ if ( type ) {
+ attributes = reduce( type.attributes, ( result, value, key ) => {
+ if ( value.source === 'meta' ) {
+ if ( result === attributes ) {
+ result = { ...result };
+ }
+
+ result[ key ] = getPostMeta( state, value.meta );
+ }
+
+ return result;
+ }, attributes );
+ }
+
+ return attributes;
+ },
+ ( state, clientId ) => [
+ state.blocks.byClientId[ clientId ],
+ state.blocks.attributes[ clientId ],
+ getPostMeta( state ),
+ ]
+);
+
+/**
+ * Returns a block given its client ID. This is a parsed copy of the block,
+ * containing its `blockName`, `clientId`, and current `attributes` state. This
+ * is not the block's registration settings, which must be retrieved from the
+ * blocks module registration store.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {Object} Parsed block object.
+ */
+export const getBlock = createSelector(
+ ( state, clientId ) => {
+ const block = state.blocks.byClientId[ clientId ];
+ if ( ! block ) {
+ return null;
+ }
+
+ return {
+ ...block,
+ attributes: getBlockAttributes( state, clientId ),
+ innerBlocks: getBlocks( state, clientId ),
+ };
+ },
+ ( state, clientId ) => [
+ ...getBlockAttributes.getDependants( state, clientId ),
+ getBlockDependantsCacheBust( state, clientId ),
+ ]
+);
+
+export const __unstableGetBlockWithoutInnerBlocks = createSelector(
+ ( state, clientId ) => {
+ const block = state.blocks.byClientId[ clientId ];
+ if ( ! block ) {
+ return null;
+ }
+
+ return {
+ ...block,
+ attributes: getBlockAttributes( state, clientId ),
+ };
+ },
+ ( state, clientId ) => [
+ state.blocks.byClientId[ clientId ],
+ ...getBlockAttributes.getDependants( state, clientId ),
+ ]
+);
+
+/**
+ * Returns all block objects for the current post being edited as an array in
+ * the order they appear in the post.
+ *
+ * Note: It's important to memoize this selector to avoid return a new instance
+ * on each call
+ *
+ * @param {Object} state Editor state.
+ * @param {?String} rootClientId Optional root client ID of block list.
+ *
+ * @return {Object[]} Post blocks.
+ */
+export const getBlocks = createSelector(
+ ( state, rootClientId ) => {
+ return map(
+ getBlockOrder( state, rootClientId ),
+ ( clientId ) => getBlock( state, clientId )
+ );
+ },
+ ( state ) => [
+ state.blocks.byClientId,
+ state.blocks.order,
+ state.blocks.attributes,
+ ]
+);
+
+/**
+ * Returns an array containing the clientIds of all descendants
+ * of the blocks given.
+ *
+ * @param {Object} state Global application state.
+ * @param {Array} clientIds Array of blocks to inspect.
+ *
+ * @return {Array} ids of descendants.
+ */
+export const getClientIdsOfDescendants = ( state, clientIds ) => flatMap( clientIds, ( clientId ) => {
+ const descendants = getBlockOrder( state, clientId );
+ return [ ...descendants, ...getClientIdsOfDescendants( state, descendants ) ];
+} );
+
+/**
+ * Returns an array containing the clientIds of the top-level blocks
+ * and their descendants of any depth (for nested blocks).
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {Array} ids of top-level and descendant blocks.
+ */
+export const getClientIdsWithDescendants = createSelector(
+ ( state ) => {
+ const topLevelIds = getBlockOrder( state );
+ return [ ...topLevelIds, ...getClientIdsOfDescendants( state, topLevelIds ) ];
+ },
+ ( state ) => [
+ state.blocks.order,
+ ]
+);
+
+/**
+ * Returns the total number of blocks, or the total number of blocks with a specific name in a post.
+ * The number returned includes nested blocks.
+ *
+ * @param {Object} state Global application state.
+ * @param {?String} blockName Optional block name, if specified only blocks of that type will be counted.
+ *
+ * @return {number} Number of blocks in the post, or number of blocks with name equal to blockName.
+ */
+export const getGlobalBlockCount = createSelector(
+ ( state, blockName ) => {
+ const clientIds = getClientIdsWithDescendants( state );
+ if ( ! blockName ) {
+ return clientIds.length;
+ }
+ return reduce( clientIds, ( count, clientId ) => {
+ const block = state.blocks.byClientId[ clientId ];
+ return block.name === blockName ? count + 1 : count;
+ }, 0 );
+ },
+ ( state ) => [
+ state.blocks.order,
+ state.blocks.byClientId,
+ ]
+);
+
+/**
+ * Given an array of block client IDs, returns the corresponding array of block
+ * objects.
+ *
+ * @param {Object} state Editor state.
+ * @param {string[]} clientIds Client IDs for which blocks are to be returned.
+ *
+ * @return {WPBlock[]} Block objects.
+ */
+export const getBlocksByClientId = createSelector(
+ ( state, clientIds ) => map(
+ castArray( clientIds ),
+ ( clientId ) => getBlock( state, clientId )
+ ),
+ ( state ) => [
+ getPostMeta( state ),
+ state.blocks.byClientId,
+ state.blocks.order,
+ state.blocks.attributes,
+ ]
+);
+
+/**
+ * Returns the number of blocks currently present in the post.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {number} Number of blocks in the post.
+ */
+export function getBlockCount( state, rootClientId ) {
+ return getBlockOrder( state, rootClientId ).length;
+}
+
+/**
+ * Returns the current block selection start. This value may be null, and it
+ * may represent either a singular block selection or multi-selection start.
+ * A selection is singular if its start and end match.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {?string} Client ID of block selection start.
+ */
+export function getBlockSelectionStart( state ) {
+ return state.blockSelection.start;
+}
+
+/**
+ * Returns the current block selection end. This value may be null, and it
+ * may represent either a singular block selection or multi-selection end.
+ * A selection is singular if its start and end match.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {?string} Client ID of block selection end.
+ */
+export function getBlockSelectionEnd( state ) {
+ return state.blockSelection.end;
+}
+
+/**
+ * Returns the number of blocks currently selected in the post.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {number} Number of blocks selected in the post.
+ */
+export function getSelectedBlockCount( state ) {
+ const multiSelectedBlockCount = getMultiSelectedBlockClientIds( state ).length;
+
+ if ( multiSelectedBlockCount ) {
+ return multiSelectedBlockCount;
+ }
+
+ return state.blockSelection.start ? 1 : 0;
+}
+
+/**
+ * Returns true if there is a single selected block, or false otherwise.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {boolean} Whether a single block is selected.
+ */
+export function hasSelectedBlock( state ) {
+ const { start, end } = state.blockSelection;
+ return !! start && start === end;
+}
+
+/**
+ * Returns the currently selected block client ID, or null if there is no
+ * selected block.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {?string} Selected block client ID.
+ */
+export function getSelectedBlockClientId( state ) {
+ const { start, end } = state.blockSelection;
+ // We need to check the block exists because the current blockSelection
+ // reducer doesn't take into account when blocks are reset via undo. To be
+ // removed when that's fixed.
+ return start && start === end && !! state.blocks.byClientId[ start ] ? start : null;
+}
+
+/**
+ * Returns the currently selected block, or null if there is no selected block.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {?Object} Selected block.
+ */
+export function getSelectedBlock( state ) {
+ const clientId = getSelectedBlockClientId( state );
+ return clientId ? getBlock( state, clientId ) : null;
+}
+
+/**
+ * Given a block client ID, returns the root block from which the block is
+ * nested, an empty string for top-level blocks, or null if the block does not
+ * exist.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block from which to find root client ID.
+ *
+ * @return {?string} Root client ID, if exists
+ */
+export const getBlockRootClientId = createSelector(
+ ( state, clientId ) => {
+ const { order } = state.blocks;
+
+ for ( const rootClientId in order ) {
+ if ( includes( order[ rootClientId ], clientId ) ) {
+ return rootClientId;
+ }
+ }
+
+ return null;
+ },
+ ( state ) => [
+ state.blocks.order,
+ ]
+);
+
+/**
+ * Given a block client ID, returns the root of the hierarchy from which the block is nested, return the block itself for root level blocks.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block from which to find root client ID.
+ *
+ * @return {string} Root client ID
+ */
+export const getBlockHierarchyRootClientId = createSelector(
+ ( state, clientId ) => {
+ let rootClientId = clientId;
+ let current = clientId;
+ while ( rootClientId ) {
+ current = rootClientId;
+ rootClientId = getBlockRootClientId( state, current );
+ }
+
+ return current;
+ },
+ ( state ) => [
+ state.blocks.order,
+ ]
+);
+
+/**
+ * Returns the client ID of the block adjacent one at the given reference
+ * startClientId and modifier directionality. Defaults start startClientId to
+ * the selected block, and direction as next block. Returns null if there is no
+ * adjacent block.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} startClientId Optional client ID of block from which to
+ * search.
+ * @param {?number} modifier Directionality multiplier (1 next, -1
+ * previous).
+ *
+ * @return {?string} Return the client ID of the block, or null if none exists.
+ */
+export function getAdjacentBlockClientId( state, startClientId, modifier = 1 ) {
+ // Default to selected block.
+ if ( startClientId === undefined ) {
+ startClientId = getSelectedBlockClientId( state );
+ }
+
+ // Try multi-selection starting at extent based on modifier.
+ if ( startClientId === undefined ) {
+ if ( modifier < 0 ) {
+ startClientId = getFirstMultiSelectedBlockClientId( state );
+ } else {
+ startClientId = getLastMultiSelectedBlockClientId( state );
+ }
+ }
+
+ // Validate working start client ID.
+ if ( ! startClientId ) {
+ return null;
+ }
+
+ // Retrieve start block root client ID, being careful to allow the falsey
+ // empty string top-level root by explicitly testing against null.
+ const rootClientId = getBlockRootClientId( state, startClientId );
+ if ( rootClientId === null ) {
+ return null;
+ }
+
+ const { order } = state.blocks;
+ const orderSet = order[ rootClientId ];
+ const index = orderSet.indexOf( startClientId );
+ const nextIndex = ( index + ( 1 * modifier ) );
+
+ // Block was first in set and we're attempting to get previous.
+ if ( nextIndex < 0 ) {
+ return null;
+ }
+
+ // Block was last in set and we're attempting to get next.
+ if ( nextIndex === orderSet.length ) {
+ return null;
+ }
+
+ // Assume incremented index is within the set.
+ return orderSet[ nextIndex ];
+}
+
+/**
+ * Returns the previous block's client ID from the given reference start ID.
+ * Defaults start to the selected block. Returns null if there is no previous
+ * block.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} startClientId Optional client ID of block from which to
+ * search.
+ *
+ * @return {?string} Adjacent block's client ID, or null if none exists.
+ */
+export function getPreviousBlockClientId( state, startClientId ) {
+ return getAdjacentBlockClientId( state, startClientId, -1 );
+}
+
+/**
+ * Returns the next block's client ID from the given reference start ID.
+ * Defaults start to the selected block. Returns null if there is no next
+ * block.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} startClientId Optional client ID of block from which to
+ * search.
+ *
+ * @return {?string} Adjacent block's client ID, or null if none exists.
+ */
+export function getNextBlockClientId( state, startClientId ) {
+ return getAdjacentBlockClientId( state, startClientId, 1 );
+}
+
+/**
+ * Returns the initial caret position for the selected block.
+ * This position is to used to position the caret properly when the selected block changes.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {?Object} Selected block.
+ */
+export function getSelectedBlocksInitialCaretPosition( state ) {
+ const { start, end } = state.blockSelection;
+ if ( start !== end || ! start ) {
+ return null;
+ }
+
+ return state.blockSelection.initialPosition;
+}
+
+/**
+ * Returns the current multi-selection set of block client IDs, or an empty
+ * array if there is no multi-selection.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {Array} Multi-selected block client IDs.
+ */
+export const getMultiSelectedBlockClientIds = createSelector(
+ ( state ) => {
+ const { start, end } = state.blockSelection;
+ if ( start === end ) {
+ return [];
+ }
+
+ // Retrieve root client ID to aid in retrieving relevant nested block
+ // order, being careful to allow the falsey empty string top-level root
+ // by explicitly testing against null.
+ const rootClientId = getBlockRootClientId( state, start );
+ if ( rootClientId === null ) {
+ return [];
+ }
+
+ const blockOrder = getBlockOrder( state, rootClientId );
+ const startIndex = blockOrder.indexOf( start );
+ const endIndex = blockOrder.indexOf( end );
+
+ if ( startIndex > endIndex ) {
+ return blockOrder.slice( endIndex, startIndex + 1 );
+ }
+
+ return blockOrder.slice( startIndex, endIndex + 1 );
+ },
+ ( state ) => [
+ state.blocks.order,
+ state.blockSelection.start,
+ state.blockSelection.end,
+ ],
+);
+
+/**
+ * Returns the current multi-selection set of blocks, or an empty array if
+ * there is no multi-selection.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {Array} Multi-selected block objects.
+ */
+export const getMultiSelectedBlocks = createSelector(
+ ( state ) => {
+ const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds( state );
+ if ( ! multiSelectedBlockClientIds.length ) {
+ return EMPTY_ARRAY;
+ }
+
+ return multiSelectedBlockClientIds.map( ( clientId ) => getBlock( state, clientId ) );
+ },
+ ( state ) => [
+ ...getMultiSelectedBlockClientIds.getDependants( state ),
+ state.blocks.byClientId,
+ state.blocks.order,
+ state.blocks.attributes,
+ getPostMeta( state ),
+ ]
+);
+
+/**
+ * Returns the client ID of the first block in the multi-selection set, or null
+ * if there is no multi-selection.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {?string} First block client ID in the multi-selection set.
+ */
+export function getFirstMultiSelectedBlockClientId( state ) {
+ return first( getMultiSelectedBlockClientIds( state ) ) || null;
+}
+
+/**
+ * Returns the client ID of the last block in the multi-selection set, or null
+ * if there is no multi-selection.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {?string} Last block client ID in the multi-selection set.
+ */
+export function getLastMultiSelectedBlockClientId( state ) {
+ return last( getMultiSelectedBlockClientIds( state ) ) || null;
+}
+
+/**
+ * Checks if possibleAncestorId is an ancestor of possibleDescendentId.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} possibleAncestorId Possible ancestor client ID.
+ * @param {string} possibleDescendentId Possible descent client ID.
+ *
+ * @return {boolean} True if possibleAncestorId is an ancestor
+ * of possibleDescendentId, and false otherwise.
+ */
+const isAncestorOf = createSelector(
+ ( state, possibleAncestorId, possibleDescendentId ) => {
+ let idToCheck = possibleDescendentId;
+ while ( possibleAncestorId !== idToCheck && idToCheck ) {
+ idToCheck = getBlockRootClientId( state, idToCheck );
+ }
+ return possibleAncestorId === idToCheck;
+ },
+ ( state ) => [
+ state.blocks.order,
+ ],
+);
+
+/**
+ * Returns true if a multi-selection exists, and the block corresponding to the
+ * specified client ID is the first block of the multi-selection set, or false
+ * otherwise.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {boolean} Whether block is first in multi-selection.
+ */
+export function isFirstMultiSelectedBlock( state, clientId ) {
+ return getFirstMultiSelectedBlockClientId( state ) === clientId;
+}
+
+/**
+ * Returns true if the client ID occurs within the block multi-selection, or
+ * false otherwise.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {boolean} Whether block is in multi-selection set.
+ */
+export function isBlockMultiSelected( state, clientId ) {
+ return getMultiSelectedBlockClientIds( state ).indexOf( clientId ) !== -1;
+}
+
+/**
+ * Returns true if an ancestor of the block is multi-selected, or false
+ * otherwise.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {boolean} Whether an ancestor of the block is in multi-selection
+ * set.
+ */
+export const isAncestorMultiSelected = createSelector(
+ ( state, clientId ) => {
+ let ancestorClientId = clientId;
+ let isMultiSelected = false;
+ while ( ancestorClientId && ! isMultiSelected ) {
+ ancestorClientId = getBlockRootClientId( state, ancestorClientId );
+ isMultiSelected = isBlockMultiSelected( state, ancestorClientId );
+ }
+ return isMultiSelected;
+ },
+ ( state ) => [
+ state.blocks.order,
+ state.blockSelection.start,
+ state.blockSelection.end,
+ ],
+);
+/**
+ * Returns the client ID of the block which begins the multi-selection set, or
+ * null if there is no multi-selection.
+ *
+ * This is not necessarily the first client ID in the selection.
+ *
+ * @see getFirstMultiSelectedBlockClientId
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {?string} Client ID of block beginning multi-selection.
+ */
+export function getMultiSelectedBlocksStartClientId( state ) {
+ const { start, end } = state.blockSelection;
+ if ( start === end ) {
+ return null;
+ }
+ return start || null;
+}
+
+/**
+ * Returns the client ID of the block which ends the multi-selection set, or
+ * null if there is no multi-selection.
+ *
+ * This is not necessarily the last client ID in the selection.
+ *
+ * @see getLastMultiSelectedBlockClientId
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {?string} Client ID of block ending multi-selection.
+ */
+export function getMultiSelectedBlocksEndClientId( state ) {
+ const { start, end } = state.blockSelection;
+ if ( start === end ) {
+ return null;
+ }
+ return end || null;
+}
+
+/**
+ * Returns an array containing all block client IDs in the editor in the order
+ * they appear. Optionally accepts a root client ID of the block list for which
+ * the order should be returned, defaulting to the top-level block order.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {Array} Ordered client IDs of editor blocks.
+ */
+export function getBlockOrder( state, rootClientId ) {
+ return state.blocks.order[ rootClientId || '' ] || EMPTY_ARRAY;
+}
+
+/**
+ * Returns the index at which the block corresponding to the specified client
+ * ID occurs within the block order, or `-1` if the block does not exist.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {number} Index at which block exists in order.
+ */
+export function getBlockIndex( state, clientId, rootClientId ) {
+ return getBlockOrder( state, rootClientId ).indexOf( clientId );
+}
+
+/**
+ * Returns true if the block corresponding to the specified client ID is
+ * currently selected and no multi-selection exists, or false otherwise.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {boolean} Whether block is selected and multi-selection exists.
+ */
+export function isBlockSelected( state, clientId ) {
+ const { start, end } = state.blockSelection;
+
+ if ( start !== end ) {
+ return false;
+ }
+
+ return start === clientId;
+}
+
+/**
+ * Returns true if one of the block's inner blocks is selected.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ * @param {boolean} deep Perform a deep check.
+ *
+ * @return {boolean} Whether the block as an inner block selected
+ */
+export function hasSelectedInnerBlock( state, clientId, deep = false ) {
+ return some(
+ getBlockOrder( state, clientId ),
+ ( innerClientId ) => (
+ isBlockSelected( state, innerClientId ) ||
+ isBlockMultiSelected( state, innerClientId ) ||
+ ( deep && hasSelectedInnerBlock( state, innerClientId, deep ) )
+ )
+ );
+}
+
+/**
+ * Returns true if the block corresponding to the specified client ID is
+ * currently selected but isn't the last of the selected blocks. Here "last"
+ * refers to the block sequence in the document, _not_ the sequence of
+ * multi-selection, which is why `state.blockSelection.end` isn't used.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {boolean} Whether block is selected and not the last in the
+ * selection.
+ */
+export function isBlockWithinSelection( state, clientId ) {
+ if ( ! clientId ) {
+ return false;
+ }
+
+ const clientIds = getMultiSelectedBlockClientIds( state );
+ const index = clientIds.indexOf( clientId );
+ return index > -1 && index < clientIds.length - 1;
+}
+
+/**
+ * Returns true if a multi-selection has been made, or false otherwise.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {boolean} Whether multi-selection has been made.
+ */
+export function hasMultiSelection( state ) {
+ const { start, end } = state.blockSelection;
+ return start !== end;
+}
+
+/**
+ * Whether in the process of multi-selecting or not. This flag is only true
+ * while the multi-selection is being selected (by mouse move), and is false
+ * once the multi-selection has been settled.
+ *
+ * @see hasMultiSelection
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {boolean} True if multi-selecting, false if not.
+ */
+export function isMultiSelecting( state ) {
+ return state.blockSelection.isMultiSelecting;
+}
+
+/**
+ * Selector that returns if multi-selection is enabled or not.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {boolean} True if it should be possible to multi-select blocks, false if multi-selection is disabled.
+ */
+export function isSelectionEnabled( state ) {
+ return state.blockSelection.isEnabled;
+}
+
+/**
+ * Returns the block's editing mode, defaulting to "visual" if not explicitly
+ * assigned.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} clientId Block client ID.
+ *
+ * @return {Object} Block editing mode.
+ */
+export function getBlockMode( state, clientId ) {
+ return state.blocksMode[ clientId ] || 'visual';
+}
+
+/**
+ * Returns true if the user is typing, or false otherwise.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {boolean} Whether user is typing.
+ */
+export function isTyping( state ) {
+ return state.isTyping;
+}
+
+/**
+ * Returns true if the caret is within formatted text, or false otherwise.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {boolean} Whether the caret is within formatted text.
+ */
+export function isCaretWithinFormattedText( state ) {
+ return state.isCaretWithinFormattedText;
+}
+
+/**
+ * Returns the insertion point, the index at which the new inserted block would
+ * be placed. Defaults to the last index.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {Object} Insertion point object with `rootClientId`, `index`.
+ */
+export function getBlockInsertionPoint( state ) {
+ let rootClientId, index;
+
+ const { insertionPoint, blockSelection } = state;
+ if ( insertionPoint !== null ) {
+ return insertionPoint;
+ }
+
+ const { end } = blockSelection;
+ if ( end ) {
+ rootClientId = getBlockRootClientId( state, end ) || undefined;
+ index = getBlockIndex( state, end, rootClientId ) + 1;
+ } else {
+ index = getBlockOrder( state ).length;
+ }
+
+ return { rootClientId, index };
+}
+
+/**
+ * Returns true if we should show the block insertion point.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {?boolean} Whether the insertion point is visible or not.
+ */
+export function isBlockInsertionPointVisible( state ) {
+ return state.insertionPoint !== null;
+}
+
+/**
+ * Returns whether the blocks matches the template or not.
+ *
+ * @param {boolean} state
+ * @return {?boolean} Whether the template is valid or not.
+ */
+export function isValidTemplate( state ) {
+ return state.template.isValid;
+}
+
+/**
+ * Returns the defined block template
+ *
+ * @param {boolean} state
+ * @return {?Array} Block Template
+ */
+export function getTemplate( state ) {
+ return state.settings.template;
+}
+
+/**
+ * Returns the defined block template lock. Optionally accepts a root block
+ * client ID as context, otherwise defaulting to the global context.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} rootClientId Optional block root client ID.
+ *
+ * @return {?string} Block Template Lock
+ */
+export function getTemplateLock( state, rootClientId ) {
+ if ( ! rootClientId ) {
+ return state.settings.templateLock;
+ }
+
+ const blockListSettings = getBlockListSettings( state, rootClientId );
+ if ( ! blockListSettings ) {
+ return null;
+ }
+
+ return blockListSettings.templateLock;
+}
+
+/**
+ * Determines if the given block type is allowed to be inserted into the block list.
+ * This function is not exported and not memoized because using a memoized selector
+ * inside another memoized selector is just a waste of time.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} blockName The name of the block type, e.g.' core/paragraph'.
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {boolean} Whether the given block type is allowed to be inserted.
+ */
+const canInsertBlockTypeUnmemoized = ( state, blockName, rootClientId = null ) => {
+ const checkAllowList = ( list, item, defaultResult = null ) => {
+ if ( isBoolean( list ) ) {
+ return list;
+ }
+ if ( isArray( list ) ) {
+ return includes( list, item );
+ }
+ return defaultResult;
+ };
+
+ const blockType = getBlockType( blockName );
+ if ( ! blockType ) {
+ return false;
+ }
+
+ const { allowedBlockTypes } = getEditorSettings( state );
+
+ const isBlockAllowedInEditor = checkAllowList( allowedBlockTypes, blockName, true );
+ if ( ! isBlockAllowedInEditor ) {
+ return false;
+ }
+
+ const isLocked = !! getTemplateLock( state, rootClientId );
+ if ( isLocked ) {
+ return false;
+ }
+
+ const parentBlockListSettings = getBlockListSettings( state, rootClientId );
+ const parentAllowedBlocks = get( parentBlockListSettings, [ 'allowedBlocks' ] );
+ const hasParentAllowedBlock = checkAllowList( parentAllowedBlocks, blockName );
+
+ const blockAllowedParentBlocks = blockType.parent;
+ const parentName = getBlockName( state, rootClientId );
+ const hasBlockAllowedParent = checkAllowList( blockAllowedParentBlocks, parentName );
+
+ if ( hasParentAllowedBlock !== null && hasBlockAllowedParent !== null ) {
+ return hasParentAllowedBlock || hasBlockAllowedParent;
+ } else if ( hasParentAllowedBlock !== null ) {
+ return hasParentAllowedBlock;
+ } else if ( hasBlockAllowedParent !== null ) {
+ return hasBlockAllowedParent;
+ }
+
+ return true;
+};
+
+/**
+ * Determines if the given block type is allowed to be inserted into the block list.
+ *
+ * @param {Object} state Editor state.
+ * @param {string} blockName The name of the block type, e.g.' core/paragraph'.
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {boolean} Whether the given block type is allowed to be inserted.
+ */
+export const canInsertBlockType = createSelector(
+ canInsertBlockTypeUnmemoized,
+ ( state, blockName, rootClientId ) => [
+ state.blockListSettings[ rootClientId ],
+ state.blocks.byClientId[ rootClientId ],
+ state.settings.allowedBlockTypes,
+ state.settings.templateLock,
+ ],
+);
+
+/**
+ * Returns information about how recently and frequently a block has been inserted.
+ *
+ * @param {Object} state Global application state.
+ * @param {string} id A string which identifies the insert, e.g. 'core/block/12'
+ *
+ * @return {?{ time: number, count: number }} An object containing `time` which is when the last
+ * insert occurred as a UNIX epoch, and `count` which is
+ * the number of inserts that have occurred.
+ */
+function getInsertUsage( state, id ) {
+ return state.preferences.insertUsage[ id ] || null;
+}
+
+/**
+ * Returns whether we can show a block type in the inserter
+ *
+ * @param {Object} state Global State
+ * @param {Object} blockType BlockType
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {boolean} Whether the given block type is allowed to be shown in the inserter.
+ */
+const canIncludeBlockTypeInInserter = ( state, blockType, rootClientId ) => {
+ if ( ! hasBlockSupport( blockType, 'inserter', true ) ) {
+ return false;
+ }
+
+ return canInsertBlockTypeUnmemoized( state, blockType.name, rootClientId );
+};
+
+/**
+ * Returns whether we can show a reusable block in the inserter
+ *
+ * @param {Object} state Global State
+ * @param {Object} reusableBlock Reusable block object
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {boolean} Whether the given block type is allowed to be shown in the inserter.
+ */
+const canIncludeReusableBlockInInserter = ( state, reusableBlock, rootClientId ) => {
+ if ( ! canInsertBlockTypeUnmemoized( state, 'core/block', rootClientId ) ) {
+ return false;
+ }
+
+ const referencedBlockName = getBlockName( state, reusableBlock.clientId );
+ if ( ! referencedBlockName ) {
+ return false;
+ }
+
+ const referencedBlockType = getBlockType( referencedBlockName );
+ if ( ! referencedBlockType ) {
+ return false;
+ }
+
+ if ( ! canInsertBlockTypeUnmemoized( state, referencedBlockName, rootClientId ) ) {
+ return false;
+ }
+
+ if ( isAncestorOf( state, reusableBlock.clientId, rootClientId ) ) {
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * Determines the items that appear in the inserter. Includes both static
+ * items (e.g. a regular block type) and dynamic items (e.g. a reusable block).
+ *
+ * Each item object contains what's necessary to display a button in the
+ * inserter and handle its selection.
+ *
+ * The 'utility' property indicates how useful we think an item will be to the
+ * user. There are 4 levels of utility:
+ *
+ * 1. Blocks that are contextually useful (utility = 3)
+ * 2. Blocks that have been previously inserted (utility = 2)
+ * 3. Blocks that are in the common category (utility = 1)
+ * 4. All other blocks (utility = 0)
+ *
+ * The 'frecency' property is a heuristic (https://en.wikipedia.org/wiki/Frecency)
+ * that combines block usage frequenty and recency.
+ *
+ * Items are returned ordered descendingly by their 'utility' and 'frecency'.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {Editor.InserterItem[]} Items that appear in inserter.
+ *
+ * @typedef {Object} Editor.InserterItem
+ * @property {string} id Unique identifier for the item.
+ * @property {string} name The type of block to create.
+ * @property {Object} initialAttributes Attributes to pass to the newly created block.
+ * @property {string} title Title of the item, as it appears in the inserter.
+ * @property {string} icon Dashicon for the item, as it appears in the inserter.
+ * @property {string} category Block category that the item is associated with.
+ * @property {string[]} keywords Keywords that can be searched to find this item.
+ * @property {boolean} isDisabled Whether or not the user should be prevented from inserting
+ * this item.
+ * @property {number} utility How useful we think this item is, between 0 and 3.
+ * @property {number} frecency Hueristic that combines frequency and recency.
+ */
+export const getInserterItems = createSelector(
+ ( state, rootClientId = null ) => {
+ const calculateUtility = ( category, count, isContextual ) => {
+ if ( isContextual ) {
+ return INSERTER_UTILITY_HIGH;
+ } else if ( count > 0 ) {
+ return INSERTER_UTILITY_MEDIUM;
+ } else if ( category === 'common' ) {
+ return INSERTER_UTILITY_LOW;
+ }
+ return INSERTER_UTILITY_NONE;
+ };
+
+ const calculateFrecency = ( time, count ) => {
+ if ( ! time ) {
+ return count;
+ }
+
+ // The selector is cached, which means Date.now() is the last time that the
+ // relevant state changed. This suits our needs.
+ const duration = Date.now() - time;
+
+ switch ( true ) {
+ case duration < MILLISECONDS_PER_HOUR:
+ return count * 4;
+ case duration < MILLISECONDS_PER_DAY:
+ return count * 2;
+ case duration < MILLISECONDS_PER_WEEK:
+ return count / 2;
+ default:
+ return count / 4;
+ }
+ };
+
+ const buildBlockTypeInserterItem = ( blockType ) => {
+ const id = blockType.name;
+
+ let isDisabled = false;
+ if ( ! hasBlockSupport( blockType.name, 'multiple', true ) ) {
+ isDisabled = some( getBlocksByClientId( state, getClientIdsWithDescendants( state ) ), { name: blockType.name } );
+ }
+
+ const isContextual = isArray( blockType.parent );
+ const { time, count = 0 } = getInsertUsage( state, id ) || {};
+
+ return {
+ id,
+ name: blockType.name,
+ initialAttributes: {},
+ title: blockType.title,
+ icon: blockType.icon,
+ category: blockType.category,
+ keywords: blockType.keywords,
+ isDisabled,
+ utility: calculateUtility( blockType.category, count, isContextual ),
+ frecency: calculateFrecency( time, count ),
+ hasChildBlocksWithInserterSupport: hasChildBlocksWithInserterSupport( blockType.name ),
+ };
+ };
+
+ const buildReusableBlockInserterItem = ( reusableBlock ) => {
+ const id = `core/block/${ reusableBlock.id }`;
+
+ const referencedBlockName = getBlockName( state, reusableBlock.clientId );
+ const referencedBlockType = getBlockType( referencedBlockName );
+
+ const { time, count = 0 } = getInsertUsage( state, id ) || {};
+ const utility = calculateUtility( 'reusable', count, false );
+ const frecency = calculateFrecency( time, count );
+
+ return {
+ id,
+ name: 'core/block',
+ initialAttributes: { ref: reusableBlock.id },
+ title: reusableBlock.title,
+ icon: referencedBlockType.icon,
+ category: 'reusable',
+ keywords: [],
+ isDisabled: false,
+ utility,
+ frecency,
+ };
+ };
+
+ const blockTypeInserterItems = getBlockTypes()
+ .filter( ( blockType ) => canIncludeBlockTypeInInserter( state, blockType, rootClientId ) )
+ .map( buildBlockTypeInserterItem );
+
+ const reusableBlockInserterItems = getReusableBlocks( state )
+ .filter( ( block ) => canIncludeReusableBlockInInserter( state, block, rootClientId ) )
+ .map( buildReusableBlockInserterItem );
+
+ return orderBy(
+ [ ...blockTypeInserterItems, ...reusableBlockInserterItems ],
+ [ 'utility', 'frecency' ],
+ [ 'desc', 'desc' ]
+ );
+ },
+ ( state, rootClientId ) => [
+ state.blockListSettings[ rootClientId ],
+ state.blocks.byClientId,
+ state.blocks.order,
+ state.preferences.insertUsage,
+ state.settings.allowedBlockTypes,
+ state.settings.templateLock,
+ getReusableBlocks( state ),
+ getBlockTypes(),
+ ],
+);
+
+/**
+ * Determines whether there are items to show in the inserter.
+ * @param {Object} state Editor state.
+ * @param {?string} rootClientId Optional root client ID of block list.
+ *
+ * @return {boolean} Items that appear in inserter.
+ */
+export const hasInserterItems = createSelector(
+ ( state, rootClientId = null ) => {
+ const hasBlockType = some(
+ getBlockTypes(),
+ ( blockType ) => canIncludeBlockTypeInInserter( state, blockType, rootClientId )
+ );
+ if ( hasBlockType ) {
+ return true;
+ }
+ const hasReusableBlock = some(
+ getReusableBlocks( state ),
+ ( block ) => canIncludeReusableBlockInInserter( state, block, rootClientId )
+ );
+
+ return hasReusableBlock;
+ },
+ ( state, rootClientId ) => [
+ state.blockListSettings[ rootClientId ],
+ state.blocks.byClientId,
+ state.settings.allowedBlockTypes,
+ state.settings.templateLock,
+ getReusableBlocks( state ),
+ getBlockTypes(),
+ ],
+);
+
+/**
+ * Returns the Block List settings of a block, if any exist.
+ *
+ * @param {Object} state Editor state.
+ * @param {?string} clientId Block client ID.
+ *
+ * @return {?Object} Block settings of the block if set.
+ */
+export function getBlockListSettings( state, clientId ) {
+ return state.blockListSettings[ clientId ];
+}
+
+/**
+ * Returns the editor settings.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {Object} The editor settings object.
+ */
+export function getEditorSettings( state ) {
+ return state.settings;
+}
+
+/**
+ * Returns true if the most recent block change is be considered persistent, or
+ * false otherwise. A persistent change is one committed by BlockEditorProvider
+ * via its `onChange` callback, in addition to `onInput`.
+ *
+ * @param {Object} state Block editor state.
+ *
+ * @return {boolean} Whether the most recent block change was persistent.
+ */
+export function isLastBlockChangePersistent( state ) {
+ return state.blocks.isPersistentChange;
+}
+
+/**
+ * Returns the value of a post meta from the editor settings.
+ *
+ * @param {Object} state Global application state.
+ * @param {string} key Meta Key to retrieve
+ *
+ * @return {*} Meta value
+ */
+function getPostMeta( state, key ) {
+ if ( key === undefined ) {
+ return get( state, [ 'settings', '__experimentalMetaSource', 'value' ], EMPTY_OBJECT );
+ }
+
+ return get( state, [ 'settings', '__experimentalMetaSource', 'value', key ] );
+}
+
+/**
+ * Returns the available reusable blocks
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {Array} Reusable blocks
+ */
+function getReusableBlocks( state ) {
+ return get( state, [ 'settings', '__experimentalReusableBlocks' ], EMPTY_ARRAY );
+}
diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js
new file mode 100644
index 0000000000000..3ae9039505356
--- /dev/null
+++ b/packages/block-editor/src/store/test/actions.js
@@ -0,0 +1,336 @@
+/**
+ * Internal dependencies
+ */
+import {
+ replaceBlocks,
+ startTyping,
+ stopTyping,
+ enterFormattedText,
+ exitFormattedText,
+ toggleSelection,
+ resetBlocks,
+ updateBlockAttributes,
+ updateBlock,
+ selectBlock,
+ selectPreviousBlock,
+ startMultiSelect,
+ stopMultiSelect,
+ multiSelect,
+ clearSelectedBlock,
+ replaceBlock,
+ insertBlock,
+ insertBlocks,
+ showInsertionPoint,
+ hideInsertionPoint,
+ mergeBlocks,
+ removeBlocks,
+ removeBlock,
+ toggleBlockMode,
+ updateBlockListSettings,
+} from '../actions';
+
+describe( 'actions', () => {
+ describe( 'resetBlocks', () => {
+ it( 'should return the RESET_BLOCKS actions', () => {
+ const blocks = [];
+ const result = resetBlocks( blocks );
+ expect( result ).toEqual( {
+ type: 'RESET_BLOCKS',
+ blocks,
+ } );
+ } );
+ } );
+
+ describe( 'updateBlockAttributes', () => {
+ it( 'should return the UPDATE_BLOCK_ATTRIBUTES action', () => {
+ const clientId = 'myclientid';
+ const attributes = {};
+ const result = updateBlockAttributes( clientId, attributes );
+ expect( result ).toEqual( {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId,
+ attributes,
+ } );
+ } );
+ } );
+
+ describe( 'updateBlock', () => {
+ it( 'should return the UPDATE_BLOCK action', () => {
+ const clientId = 'myclientid';
+ const updates = {};
+ const result = updateBlock( clientId, updates );
+ expect( result ).toEqual( {
+ type: 'UPDATE_BLOCK',
+ clientId,
+ updates,
+ } );
+ } );
+ } );
+
+ describe( 'selectBlock', () => {
+ it( 'should return the SELECT_BLOCK action', () => {
+ const clientId = 'myclientid';
+ const result = selectBlock( clientId, -1 );
+ expect( result ).toEqual( {
+ type: 'SELECT_BLOCK',
+ initialPosition: -1,
+ clientId,
+ } );
+ } );
+ } );
+
+ describe( 'startMultiSelect', () => {
+ it( 'should return the START_MULTI_SELECT', () => {
+ expect( startMultiSelect() ).toEqual( {
+ type: 'START_MULTI_SELECT',
+ } );
+ } );
+ } );
+
+ describe( 'stopMultiSelect', () => {
+ it( 'should return the Stop_MULTI_SELECT', () => {
+ expect( stopMultiSelect() ).toEqual( {
+ type: 'STOP_MULTI_SELECT',
+ } );
+ } );
+ } );
+ describe( 'multiSelect', () => {
+ it( 'should return MULTI_SELECT action', () => {
+ const start = 'start';
+ const end = 'end';
+ expect( multiSelect( start, end ) ).toEqual( {
+ type: 'MULTI_SELECT',
+ start,
+ end,
+ } );
+ } );
+ } );
+
+ describe( 'clearSelectedBlock', () => {
+ it( 'should return CLEAR_SELECTED_BLOCK action', () => {
+ expect( clearSelectedBlock() ).toEqual( {
+ type: 'CLEAR_SELECTED_BLOCK',
+ } );
+ } );
+ } );
+
+ describe( 'replaceBlock', () => {
+ it( 'should return the REPLACE_BLOCKS action', () => {
+ const block = {
+ clientId: 'ribs',
+ };
+
+ expect( replaceBlock( [ 'chicken' ], block ) ).toEqual( {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ block ],
+ time: expect.any( Number ),
+ } );
+ } );
+ } );
+
+ describe( 'replaceBlocks', () => {
+ it( 'should return the REPLACE_BLOCKS action', () => {
+ const blocks = [ {
+ clientId: 'ribs',
+ } ];
+
+ expect( replaceBlocks( [ 'chicken' ], blocks ) ).toEqual( {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks,
+ time: expect.any( Number ),
+ } );
+ } );
+ } );
+
+ describe( 'insertBlock', () => {
+ it( 'should return the INSERT_BLOCKS action', () => {
+ const block = {
+ clientId: 'ribs',
+ };
+ const index = 5;
+ expect( insertBlock( block, index, 'testclientid' ) ).toEqual( {
+ type: 'INSERT_BLOCKS',
+ blocks: [ block ],
+ index,
+ rootClientId: 'testclientid',
+ time: expect.any( Number ),
+ updateSelection: true,
+ } );
+ } );
+ } );
+
+ describe( 'insertBlocks', () => {
+ it( 'should return the INSERT_BLOCKS action', () => {
+ const blocks = [ {
+ clientId: 'ribs',
+ } ];
+ const index = 3;
+ expect( insertBlocks( blocks, index, 'testclientid' ) ).toEqual( {
+ type: 'INSERT_BLOCKS',
+ blocks,
+ index,
+ rootClientId: 'testclientid',
+ time: expect.any( Number ),
+ updateSelection: true,
+ } );
+ } );
+ } );
+
+ describe( 'showInsertionPoint', () => {
+ it( 'should return the SHOW_INSERTION_POINT action', () => {
+ expect( showInsertionPoint() ).toEqual( {
+ type: 'SHOW_INSERTION_POINT',
+ } );
+ } );
+ } );
+
+ describe( 'hideInsertionPoint', () => {
+ it( 'should return the HIDE_INSERTION_POINT action', () => {
+ expect( hideInsertionPoint() ).toEqual( {
+ type: 'HIDE_INSERTION_POINT',
+ } );
+ } );
+ } );
+
+ describe( 'mergeBlocks', () => {
+ it( 'should return MERGE_BLOCKS action', () => {
+ const firstBlockClientId = 'blockA';
+ const secondBlockClientId = 'blockB';
+ expect( mergeBlocks( firstBlockClientId, secondBlockClientId ) ).toEqual( {
+ type: 'MERGE_BLOCKS',
+ blocks: [ firstBlockClientId, secondBlockClientId ],
+ } );
+ } );
+ } );
+
+ describe( 'removeBlocks', () => {
+ it( 'should return REMOVE_BLOCKS action', () => {
+ const clientId = 'clientId';
+ const clientIds = [ clientId ];
+
+ const actions = Array.from( removeBlocks( clientIds ) );
+
+ expect( actions ).toEqual( [
+ selectPreviousBlock( clientId ),
+ {
+ type: 'REMOVE_BLOCKS',
+ clientIds,
+ },
+ ] );
+ } );
+ } );
+
+ describe( 'removeBlock', () => {
+ it( 'should return REMOVE_BLOCKS action', () => {
+ const clientId = 'myclientid';
+
+ const actions = Array.from( removeBlock( clientId ) );
+
+ expect( actions ).toEqual( [
+ selectPreviousBlock( clientId ),
+ {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ clientId ],
+ },
+ ] );
+ } );
+
+ it( 'should return REMOVE_BLOCKS action, opting out of remove previous', () => {
+ const clientId = 'myclientid';
+
+ const actions = Array.from( removeBlock( clientId, false ) );
+
+ expect( actions ).toEqual( [
+ {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ clientId ],
+ },
+ ] );
+ } );
+ } );
+
+ describe( 'toggleBlockMode', () => {
+ it( 'should return TOGGLE_BLOCK_MODE action', () => {
+ const clientId = 'myclientid';
+ expect( toggleBlockMode( clientId ) ).toEqual( {
+ type: 'TOGGLE_BLOCK_MODE',
+ clientId,
+ } );
+ } );
+ } );
+
+ describe( 'startTyping', () => {
+ it( 'should return the START_TYPING action', () => {
+ expect( startTyping() ).toEqual( {
+ type: 'START_TYPING',
+ } );
+ } );
+ } );
+
+ describe( 'stopTyping', () => {
+ it( 'should return the STOP_TYPING action', () => {
+ expect( stopTyping() ).toEqual( {
+ type: 'STOP_TYPING',
+ } );
+ } );
+ } );
+
+ describe( 'enterFormattedText', () => {
+ it( 'should return the ENTER_FORMATTED_TEXT action', () => {
+ expect( enterFormattedText() ).toEqual( {
+ type: 'ENTER_FORMATTED_TEXT',
+ } );
+ } );
+ } );
+
+ describe( 'exitFormattedText', () => {
+ it( 'should return the EXIT_FORMATTED_TEXT action', () => {
+ expect( exitFormattedText() ).toEqual( {
+ type: 'EXIT_FORMATTED_TEXT',
+ } );
+ } );
+ } );
+
+ describe( 'toggleSelection', () => {
+ it( 'should return the TOGGLE_SELECTION action with default value for isSelectionEnabled = true', () => {
+ expect( toggleSelection() ).toEqual( {
+ type: 'TOGGLE_SELECTION',
+ isSelectionEnabled: true,
+ } );
+ } );
+
+ it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = true as passed in the argument', () => {
+ expect( toggleSelection( true ) ).toEqual( {
+ type: 'TOGGLE_SELECTION',
+ isSelectionEnabled: true,
+ } );
+ } );
+
+ it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = false as passed in the argument', () => {
+ expect( toggleSelection( false ) ).toEqual( {
+ type: 'TOGGLE_SELECTION',
+ isSelectionEnabled: false,
+ } );
+ } );
+ } );
+
+ describe( 'updateBlockListSettings', () => {
+ it( 'should return the UPDATE_BLOCK_LIST_SETTINGS with undefined settings', () => {
+ expect( updateBlockListSettings( 'chicken' ) ).toEqual( {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: 'chicken',
+ settings: undefined,
+ } );
+ } );
+
+ it( 'should return the UPDATE_BLOCK_LIST_SETTINGS action with the passed settings', () => {
+ expect( updateBlockListSettings( 'chicken', { chicken: 'ribs' } ) ).toEqual( {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: 'chicken',
+ settings: { chicken: 'ribs' },
+ } );
+ } );
+ } );
+} );
diff --git a/packages/editor/src/store/test/array.js b/packages/block-editor/src/store/test/array.js
similarity index 100%
rename from packages/editor/src/store/test/array.js
rename to packages/block-editor/src/store/test/array.js
diff --git a/packages/block-editor/src/store/test/effects.js b/packages/block-editor/src/store/test/effects.js
new file mode 100644
index 0000000000000..090779cbad0d9
--- /dev/null
+++ b/packages/block-editor/src/store/test/effects.js
@@ -0,0 +1,280 @@
+/**
+ * External dependencies
+ */
+import { noop } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ getBlockTypes,
+ unregisterBlockType,
+ registerBlockType,
+ createBlock,
+} from '@wordpress/blocks';
+import { createRegistry } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import actions, {
+ updateEditorSettings,
+ mergeBlocks,
+ replaceBlocks,
+ resetBlocks,
+ selectBlock,
+ setTemplateValidity,
+} from '../actions';
+import effects, { validateBlocksToTemplate } from '../effects';
+import * as selectors from '../selectors';
+import reducer from '../reducer';
+import applyMiddlewares from '../middlewares';
+import '../../';
+
+describe( 'effects', () => {
+ const defaultBlockSettings = { save: () => 'Saved', category: 'common', title: 'block title' };
+
+ describe( '.MERGE_BLOCKS', () => {
+ const handler = effects.MERGE_BLOCKS;
+ const defaultGetBlock = selectors.getBlock;
+
+ afterEach( () => {
+ getBlockTypes().forEach( ( block ) => {
+ unregisterBlockType( block.name );
+ } );
+ selectors.getBlock = defaultGetBlock;
+ } );
+
+ it( 'should only focus the blockA if the blockA has no merge function', () => {
+ registerBlockType( 'core/test-block', defaultBlockSettings );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).toHaveBeenCalledTimes( 1 );
+ expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken' ) );
+ } );
+
+ it( 'should merge the blocks if blocks of the same type', () => {
+ registerBlockType( 'core/test-block', {
+ merge( attributes, attributesToMerge ) {
+ return {
+ content: attributes.content + ' ' + attributesToMerge.content,
+ };
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken' },
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: { content: 'ribs' },
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).toHaveBeenCalledTimes( 2 );
+ expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken', -1 ) );
+ expect( dispatch ).toHaveBeenCalledWith( {
+ ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken ribs' },
+ } ] ),
+ time: expect.any( Number ),
+ } );
+ } );
+
+ it( 'should not merge the blocks have different types without transformation', () => {
+ registerBlockType( 'core/test-block', {
+ merge( attributes, attributesToMerge ) {
+ return {
+ content: attributes.content + ' ' + attributesToMerge.content,
+ };
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ registerBlockType( 'core/test-block-2', defaultBlockSettings );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken' },
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block2',
+ attributes: { content: 'ribs' },
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).not.toHaveBeenCalled();
+ } );
+
+ it( 'should transform and merge the blocks', () => {
+ registerBlockType( 'core/test-block', {
+ attributes: {
+ content: {
+ type: 'string',
+ },
+ },
+ merge( attributes, attributesToMerge ) {
+ return {
+ content: attributes.content + ' ' + attributesToMerge.content,
+ };
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ registerBlockType( 'core/test-block-2', {
+ attributes: {
+ content: {
+ type: 'string',
+ },
+ },
+ transforms: {
+ to: [ {
+ type: 'block',
+ blocks: [ 'core/test-block' ],
+ transform: ( { content2 } ) => {
+ return createBlock( 'core/test-block', {
+ content: content2,
+ } );
+ },
+ } ],
+ },
+ save: noop,
+ category: 'common',
+ title: 'test block 2',
+ } );
+ const blockA = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken' },
+ };
+ const blockB = {
+ clientId: 'ribs',
+ name: 'core/test-block-2',
+ attributes: { content2: 'ribs' },
+ };
+ selectors.getBlock = ( state, clientId ) => {
+ return blockA.clientId === clientId ? blockA : blockB;
+ };
+ const dispatch = jest.fn();
+ const getState = () => ( {} );
+ handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
+
+ expect( dispatch ).toHaveBeenCalledTimes( 2 );
+ // expect( dispatch ).toHaveBeenCalledWith( focusBlock( 'chicken', { offset: -1 } ) );
+ expect( dispatch ).toHaveBeenCalledWith( {
+ ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: { content: 'chicken ribs' },
+ } ] ),
+ time: expect.any( Number ),
+ } );
+ } );
+ } );
+
+ describe( 'validateBlocksToTemplate', () => {
+ let store;
+ beforeEach( () => {
+ store = createRegistry().registerStore( 'test', {
+ actions,
+ selectors,
+ reducer,
+ } );
+ applyMiddlewares( store );
+
+ registerBlockType( 'core/test-block', defaultBlockSettings );
+ } );
+
+ afterEach( () => {
+ getBlockTypes().forEach( ( block ) => {
+ unregisterBlockType( block.name );
+ } );
+ } );
+
+ it( 'should return undefined if no template assigned', () => {
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toBe( undefined );
+ } );
+
+ it( 'should return undefined if invalid but unlocked', () => {
+ store.dispatch( updateEditorSettings( {
+ template: [
+ [ 'core/foo', {} ],
+ ],
+ } ) );
+
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toBe( undefined );
+ } );
+
+ it( 'should return undefined if locked and valid', () => {
+ store.dispatch( updateEditorSettings( {
+ template: [
+ [ 'core/test-block' ],
+ ],
+ templateLock: 'all',
+ } ) );
+
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toBe( undefined );
+ } );
+
+ it( 'should return validity set action if invalid on default state', () => {
+ store.dispatch( updateEditorSettings( {
+ template: [
+ [ 'core/foo' ],
+ ],
+ templateLock: 'all',
+ } ) );
+
+ const result = validateBlocksToTemplate( resetBlocks( [
+ createBlock( 'core/test-block' ),
+ ] ), store );
+
+ expect( result ).toEqual( setTemplateValidity( false ) );
+ } );
+ } );
+} );
diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js
new file mode 100644
index 0000000000000..6f1fae9dc0f03
--- /dev/null
+++ b/packages/block-editor/src/store/test/reducer.js
@@ -0,0 +1,1721 @@
+/**
+ * External dependencies
+ */
+import { values, noop } from 'lodash';
+import deepFreeze from 'deep-freeze';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ registerBlockType,
+ unregisterBlockType,
+ createBlock,
+} from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import {
+ hasSameKeys,
+ isUpdatingSameBlockAttribute,
+ blocks,
+ isTyping,
+ isCaretWithinFormattedText,
+ blockSelection,
+ preferences,
+ blocksMode,
+ insertionPoint,
+ template,
+ blockListSettings,
+} from '../reducer';
+
+describe( 'state', () => {
+ describe( 'hasSameKeys()', () => {
+ it( 'returns false if two objects do not have the same keys', () => {
+ const a = { foo: 10 };
+ const b = { bar: 10 };
+
+ expect( hasSameKeys( a, b ) ).toBe( false );
+ } );
+
+ it( 'returns false if two objects have the same keys', () => {
+ const a = { foo: 10 };
+ const b = { foo: 20 };
+
+ expect( hasSameKeys( a, b ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'isUpdatingSameBlockAttribute()', () => {
+ it( 'should return false if not updating block attributes', () => {
+ const action = {
+ type: 'SELECT_BLOCK',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ };
+ const previousAction = {
+ type: 'SELECT_BLOCK',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return false if last action was not updating block attributes', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'SELECT_BLOCK',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return false if not updating the same block', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ attributes: {
+ foo: 20,
+ },
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return false if not updating the same block attributes', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ bar: 20,
+ },
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return false if no previous action', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = undefined;
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
+ } );
+
+ it( 'should return true if updating the same block attributes', () => {
+ const action = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 10,
+ },
+ };
+ const previousAction = {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ attributes: {
+ foo: 20,
+ },
+ };
+
+ expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'blocks()', () => {
+ beforeAll( () => {
+ registerBlockType( 'core/test-block', {
+ save: noop,
+ edit: noop,
+ category: 'common',
+ title: 'test block',
+ } );
+ } );
+
+ afterAll( () => {
+ unregisterBlockType( 'core/test-block' );
+ } );
+
+ it( 'should return empty byClientId, attributes, order by default', () => {
+ const state = blocks( undefined, {} );
+
+ expect( state ).toEqual( {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ isPersistentChange: true,
+ } );
+ } );
+
+ it( 'should key by reset blocks clientId', () => {
+ [
+ undefined,
+ blocks( undefined, {} ),
+ ].forEach( ( original ) => {
+ const state = blocks( original, {
+ type: 'RESET_BLOCKS',
+ blocks: [ { clientId: 'bananas', innerBlocks: [] } ],
+ } );
+
+ expect( Object.keys( state.byClientId ) ).toHaveLength( 1 );
+ expect( values( state.byClientId )[ 0 ].clientId ).toBe( 'bananas' );
+ expect( state.order ).toEqual( {
+ '': [ 'bananas' ],
+ bananas: [],
+ } );
+ } );
+ } );
+
+ it( 'should key by reset blocks clientId, including inner blocks', () => {
+ const original = blocks( undefined, {} );
+ const state = blocks( original, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'bananas',
+ innerBlocks: [ { clientId: 'apples', innerBlocks: [] } ],
+ } ],
+ } );
+
+ expect( Object.keys( state.byClientId ) ).toHaveLength( 2 );
+ expect( state.order ).toEqual( {
+ '': [ 'bananas' ],
+ apples: [],
+ bananas: [ 'apples' ],
+ } );
+ } );
+
+ it( 'should insert block', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'ribs',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( state.byClientId ) ).toHaveLength( 2 );
+ expect( values( state.byClientId )[ 1 ].clientId ).toBe( 'ribs' );
+ expect( state.order ).toEqual( {
+ '': [ 'chicken', 'ribs' ],
+ chicken: [],
+ ribs: [],
+ } );
+ } );
+
+ it( 'should replace the block', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( state.byClientId ) ).toHaveLength( 1 );
+ expect( values( state.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
+ expect( values( state.byClientId )[ 0 ].clientId ).toBe( 'wings' );
+ expect( state.order ).toEqual( {
+ '': [ 'wings' ],
+ wings: [],
+ } );
+ } );
+
+ it( 'should replace the nested block', () => {
+ const nestedBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
+ const replacementBlock = createBlock( 'core/test-block' );
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+
+ const state = blocks( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ nestedBlock.clientId ],
+ blocks: [ replacementBlock ],
+ } );
+
+ expect( state.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ replacementBlock.clientId ],
+ [ replacementBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should replace the block even if the new block clientId is the same', () => {
+ const originalState = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const replacedState = blocks( originalState, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( replacedState.byClientId ) ).toHaveLength( 1 );
+ expect( values( originalState.byClientId )[ 0 ].name ).toBe( 'core/test-block' );
+ expect( values( replacedState.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
+ expect( values( replacedState.byClientId )[ 0 ].clientId ).toBe( 'chicken' );
+ expect( replacedState.order ).toEqual( {
+ '': [ 'chicken' ],
+ chicken: [],
+ } );
+
+ const nestedBlock = {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ };
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
+ const replacementNestedBlock = {
+ clientId: 'chicken',
+ name: 'core/freeform',
+ attributes: {},
+ innerBlocks: [],
+ };
+
+ const originalNestedState = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+
+ const replacedNestedState = blocks( originalNestedState, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ nestedBlock.clientId ],
+ blocks: [ replacementNestedBlock ],
+ } );
+
+ expect( replacedNestedState.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ replacementNestedBlock.clientId ],
+ [ replacementNestedBlock.clientId ]: [],
+ } );
+
+ expect( originalNestedState.byClientId.chicken.name ).toBe( 'core/test-block' );
+ expect( replacedNestedState.byClientId.chicken.name ).toBe( 'core/freeform' );
+ } );
+
+ it( 'should update the block', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ isValid: false,
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( deepFreeze( original ), {
+ type: 'UPDATE_BLOCK',
+ clientId: 'chicken',
+ updates: {
+ attributes: { content: 'ribs' },
+ isValid: true,
+ },
+ } );
+
+ expect( state.byClientId.chicken ).toEqual( {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ isValid: true,
+ } );
+
+ expect( state.attributes.chicken ).toEqual( {
+ content: 'ribs',
+ } );
+ } );
+
+ it( 'should update the reusable block reference if the temporary id is swapped', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/block',
+ attributes: {
+ ref: 'random-clientId',
+ },
+ isValid: false,
+ innerBlocks: [],
+ } ],
+ } );
+
+ const state = blocks( deepFreeze( original ), {
+ type: 'SAVE_REUSABLE_BLOCK_SUCCESS',
+ id: 'random-clientId',
+ updatedId: 3,
+ } );
+
+ expect( state.byClientId.chicken ).toEqual( {
+ clientId: 'chicken',
+ name: 'core/block',
+ isValid: false,
+ } );
+
+ expect( state.attributes.chicken ).toEqual( {
+ ref: 3,
+ } );
+ } );
+
+ it( 'should move the block up', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
+ } );
+
+ it( 'should move the nested block up', () => {
+ const movedBlock = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlock ] );
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ movedBlock.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ movedBlock.clientId, siblingBlock.clientId ],
+ [ movedBlock.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should move multiple blocks up', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'ribs', 'veggies' ],
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'ribs', 'veggies', 'chicken' ] );
+ } );
+
+ it( 'should move multiple nested blocks up', () => {
+ const movedBlockA = createBlock( 'core/test-block' );
+ const movedBlockB = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlockA, movedBlockB ] );
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ movedBlockA.clientId, movedBlockB.clientId, siblingBlock.clientId ],
+ [ movedBlockA.clientId ]: [],
+ [ movedBlockB.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should not move the first block up', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state.order ).toBe( original.order );
+ } );
+
+ it( 'should move the block down', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
+ } );
+
+ it( 'should move the nested block down', () => {
+ const movedBlock = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlock, siblingBlock ] );
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ movedBlock.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlock.clientId ],
+ [ movedBlock.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should move multiple blocks down', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ 'chicken', 'ribs' ],
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'veggies', 'chicken', 'ribs' ] );
+ } );
+
+ it( 'should move multiple nested blocks down', () => {
+ const movedBlockA = createBlock( 'core/test-block' );
+ const movedBlockB = createBlock( 'core/test-block' );
+ const siblingBlock = createBlock( 'core/test-block' );
+ const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlockA, movedBlockB, siblingBlock ] );
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ wrapperBlock ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
+ rootClientId: wrapperBlock.clientId,
+ } );
+
+ expect( state.order ).toEqual( {
+ '': [ wrapperBlock.clientId ],
+ [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlockA.clientId, movedBlockB.clientId ],
+ [ movedBlockA.clientId ]: [],
+ [ movedBlockB.clientId ]: [],
+ [ siblingBlock.clientId ]: [],
+ } );
+ } );
+
+ it( 'should not move the last block down', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCKS_DOWN',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state.order ).toBe( original.order );
+ } );
+
+ it( 'should remove the block', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'ribs' ] );
+ expect( state.order ).not.toHaveProperty( 'chicken' );
+ expect( state.byClientId ).toEqual( {
+ ribs: {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ },
+ } );
+ expect( state.attributes ).toEqual( {
+ ribs: {},
+ } );
+ } );
+
+ it( 'should remove multiple blocks', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'chicken', 'veggies' ],
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'ribs' ] );
+ expect( state.order ).not.toHaveProperty( 'chicken' );
+ expect( state.order ).not.toHaveProperty( 'veggies' );
+ expect( state.byClientId ).toEqual( {
+ ribs: {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ },
+ } );
+ expect( state.attributes ).toEqual( {
+ ribs: {},
+ } );
+ } );
+
+ it( 'should cascade remove to include inner blocks', () => {
+ const block = createBlock( 'core/test-block', {}, [
+ createBlock( 'core/test-block', {}, [
+ createBlock( 'core/test-block' ),
+ ] ),
+ ] );
+
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ block ],
+ } );
+
+ const state = blocks( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ block.clientId ],
+ } );
+
+ expect( state.byClientId ).toEqual( {} );
+ expect( state.order ).toEqual( {
+ '': [],
+ } );
+ } );
+
+ it( 'should insert at the specified index', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'loquat',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+
+ const state = blocks( original, {
+ type: 'INSERT_BLOCKS',
+ index: 1,
+ blocks: [ {
+ clientId: 'persimmon',
+ name: 'core/freeform',
+ innerBlocks: [],
+ } ],
+ } );
+
+ expect( Object.keys( state.byClientId ) ).toHaveLength( 3 );
+ expect( state.order[ '' ] ).toEqual( [ 'kumquat', 'persimmon', 'loquat' ] );
+ } );
+
+ it( 'should move block to lower index', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCK_TO_POSITION',
+ clientId: 'ribs',
+ index: 0,
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'ribs', 'chicken', 'veggies' ] );
+ } );
+
+ it( 'should move block to higher index', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCK_TO_POSITION',
+ clientId: 'ribs',
+ index: 2,
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'chicken', 'veggies', 'ribs' ] );
+ } );
+
+ it( 'should not move block if passed same index', () => {
+ const original = blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'chicken',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'ribs',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'veggies',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ const state = blocks( original, {
+ type: 'MOVE_BLOCK_TO_POSITION',
+ clientId: 'ribs',
+ index: 1,
+ } );
+
+ expect( state.order[ '' ] ).toEqual( [ 'chicken', 'ribs', 'veggies' ] );
+ } );
+
+ describe( 'blocks', () => {
+ it( 'should not reset any blocks that are not in the post', () => {
+ const actions = [
+ {
+ type: 'RESET_BLOCKS',
+ blocks: [
+ {
+ clientId: 'block1',
+ innerBlocks: [
+ { clientId: 'block11', innerBlocks: [] },
+ { clientId: 'block12', innerBlocks: [] },
+ ],
+ },
+ ],
+ },
+ {
+ type: 'RECEIVE_BLOCKS',
+ blocks: [
+ {
+ clientId: 'block2',
+ innerBlocks: [
+ { clientId: 'block21', innerBlocks: [] },
+ { clientId: 'block22', innerBlocks: [] },
+ ],
+ },
+ ],
+ },
+ ];
+ const original = deepFreeze( actions.reduce( blocks, undefined ) );
+
+ const state = blocks( original, {
+ type: 'RESET_BLOCKS',
+ blocks: [
+ {
+ clientId: 'block3',
+ innerBlocks: [
+ { clientId: 'block31', innerBlocks: [] },
+ { clientId: 'block32', innerBlocks: [] },
+ ],
+ },
+ ],
+ } );
+
+ expect( state.byClientId ).toEqual( {
+ block2: { clientId: 'block2' },
+ block21: { clientId: 'block21' },
+ block22: { clientId: 'block22' },
+ block3: { clientId: 'block3' },
+ block31: { clientId: 'block31' },
+ block32: { clientId: 'block32' },
+ } );
+ } );
+
+ describe( 'byClientId', () => {
+ it( 'should ignore updates to non-existent block', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [],
+ } ) );
+ const state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.byClientId ).toBe( original.byClientId );
+ } );
+
+ it( 'should return with same reference if no changes in updates', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.byClientId ).toBe( state.byClientId );
+ } );
+ } );
+
+ describe( 'attributes', () => {
+ it( 'should return with attribute block updates', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.attributes.kumquat.updated ).toBe( true );
+ } );
+
+ it( 'should accumulate attribute block updates', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ moreUpdated: true,
+ },
+ } );
+
+ expect( state.attributes.kumquat ).toEqual( {
+ updated: true,
+ moreUpdated: true,
+ } );
+ } );
+
+ it( 'should ignore updates to non-existent block', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [],
+ } ) );
+ const state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.attributes ).toBe( original.attributes );
+ } );
+
+ it( 'should return with same reference if no changes in updates', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ innerBlocks: [],
+ } ],
+ } ) );
+ const state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.attributes ).toBe( state.attributes );
+ } );
+ } );
+
+ describe( 'isPersistentChange', () => {
+ it( 'should consider any non-exempt block change as persistent', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [],
+ } ) );
+
+ const state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.isPersistentChange ).toBe( true );
+ } );
+
+ it( 'should consider same block attribute update as exempt', () => {
+ const original = deepFreeze( blocks( undefined, {
+ type: 'RESET_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } ) );
+ let state = blocks( original, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: false,
+ },
+ } );
+
+ state = blocks( state, {
+ type: 'UPDATE_BLOCK_ATTRIBUTES',
+ clientId: 'kumquat',
+ attributes: {
+ updated: true,
+ },
+ } );
+
+ expect( state.isPersistentChange ).toBe( false );
+ } );
+ } );
+ } );
+ } );
+
+ describe( 'insertionPoint', () => {
+ it( 'should default to null', () => {
+ const state = insertionPoint( undefined, {} );
+
+ expect( state ).toBe( null );
+ } );
+
+ it( 'should set insertion point', () => {
+ const state = insertionPoint( null, {
+ type: 'SHOW_INSERTION_POINT',
+ rootClientId: 'clientId1',
+ index: 0,
+ } );
+
+ expect( state ).toEqual( {
+ rootClientId: 'clientId1',
+ index: 0,
+ } );
+ } );
+
+ it( 'should clear the insertion point', () => {
+ const original = deepFreeze( {
+ rootClientId: 'clientId1',
+ index: 0,
+ } );
+ const state = insertionPoint( original, {
+ type: 'HIDE_INSERTION_POINT',
+ } );
+
+ expect( state ).toBe( null );
+ } );
+ } );
+
+ describe( 'isTyping()', () => {
+ it( 'should set the typing flag to true', () => {
+ const state = isTyping( false, {
+ type: 'START_TYPING',
+ } );
+
+ expect( state ).toBe( true );
+ } );
+
+ it( 'should set the typing flag to false', () => {
+ const state = isTyping( false, {
+ type: 'STOP_TYPING',
+ } );
+
+ expect( state ).toBe( false );
+ } );
+ } );
+
+ describe( 'isCaretWithinFormattedText()', () => {
+ it( 'should set the flag to true', () => {
+ const state = isCaretWithinFormattedText( false, {
+ type: 'ENTER_FORMATTED_TEXT',
+ } );
+
+ expect( state ).toBe( true );
+ } );
+
+ it( 'should set the flag to false', () => {
+ const state = isCaretWithinFormattedText( true, {
+ type: 'EXIT_FORMATTED_TEXT',
+ } );
+
+ expect( state ).toBe( false );
+ } );
+ } );
+
+ describe( 'blockSelection()', () => {
+ it( 'should return with block clientId as selected', () => {
+ const state = blockSelection( undefined, {
+ type: 'SELECT_BLOCK',
+ clientId: 'kumquat',
+ initialPosition: -1,
+ } );
+
+ expect( state ).toEqual( {
+ start: 'kumquat',
+ end: 'kumquat',
+ initialPosition: -1,
+ isMultiSelecting: false,
+ isEnabled: true,
+ } );
+ } );
+
+ it( 'should set multi selection', () => {
+ const original = deepFreeze( { isMultiSelecting: false } );
+ const state = blockSelection( original, {
+ type: 'MULTI_SELECT',
+ start: 'ribs',
+ end: 'chicken',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should set continuous multi selection', () => {
+ const original = deepFreeze( { isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'MULTI_SELECT',
+ start: 'ribs',
+ end: 'chicken',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: true,
+ } );
+ } );
+
+ it( 'should start multi selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: false } );
+ const state = blockSelection( original, {
+ type: 'START_MULTI_SELECT',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'ribs',
+ initialPosition: null,
+ isMultiSelecting: true,
+ } );
+ } );
+
+ it( 'should return same reference if already multi-selecting', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'START_MULTI_SELECT',
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should end multi selection with selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'STOP_MULTI_SELECT',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should return same reference if already ended multi-selecting', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: false } );
+ const state = blockSelection( original, {
+ type: 'STOP_MULTI_SELECT',
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should end multi selection without selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
+ const state = blockSelection( original, {
+ type: 'STOP_MULTI_SELECT',
+ } );
+
+ expect( state ).toEqual( {
+ start: 'ribs',
+ end: 'ribs',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should not update the state if the block is already selected', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
+
+ const state1 = blockSelection( original, {
+ type: 'SELECT_BLOCK',
+ clientId: 'ribs',
+ } );
+
+ expect( state1 ).toBe( original );
+ } );
+
+ it( 'should unset multi selection', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
+
+ const state1 = blockSelection( original, {
+ type: 'CLEAR_SELECTED_BLOCK',
+ } );
+
+ expect( state1 ).toEqual( {
+ start: null,
+ end: null,
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should return same reference if clearing selection but no selection', () => {
+ const original = deepFreeze( { start: null, end: null, isMultiSelecting: false } );
+
+ const state1 = blockSelection( original, {
+ type: 'CLEAR_SELECTED_BLOCK',
+ } );
+
+ expect( state1 ).toBe( original );
+ } );
+
+ it( 'should select inserted block', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
+
+ const state3 = blockSelection( original, {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'ribs',
+ name: 'core/freeform',
+ } ],
+ updateSelection: true,
+ } );
+
+ expect( state3 ).toEqual( {
+ start: 'ribs',
+ end: 'ribs',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should not select inserted block if updateSelection flag is false', () => {
+ const original = deepFreeze( { start: 'a', end: 'b' } );
+
+ const state3 = blockSelection( original, {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'ribs',
+ name: 'core/freeform',
+ } ],
+ updateSelection: false,
+ } );
+
+ expect( state3 ).toEqual( {
+ start: 'a',
+ end: 'b',
+ } );
+ } );
+
+ it( 'should not update the state if the block moved is already selected', () => {
+ const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
+ const state = blockSelection( original, {
+ type: 'MOVE_BLOCKS_UP',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should replace the selected block', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ } ],
+ } );
+
+ expect( state ).toEqual( {
+ start: 'wings',
+ end: 'wings',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should not replace the selected block if we keep it at the end when replacing blocks', () => {
+ const original = deepFreeze( { start: 'wings', end: 'wings' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'wings' ],
+ blocks: [
+ {
+ clientId: 'chicken',
+ name: 'core/freeform',
+ },
+ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ } ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should replace the selected block if we keep it not at the end when replacing blocks', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [
+ {
+ clientId: 'chicken',
+ name: 'core/freeform',
+ },
+ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ } ],
+ } );
+
+ expect( state ).toEqual( {
+ start: 'wings',
+ end: 'wings',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should reset if replacing with empty set', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ blocks: [],
+ } );
+
+ expect( state ).toEqual( {
+ start: null,
+ end: null,
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should keep the selected block', () => {
+ const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
+ const state = blockSelection( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'ribs' ],
+ blocks: [ {
+ clientId: 'wings',
+ name: 'core/freeform',
+ } ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should remove the selection if we are removing the selected block', () => {
+ const original = deepFreeze( {
+ start: 'chicken',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ const state = blockSelection( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'chicken' ],
+ } );
+
+ expect( state ).toEqual( {
+ start: null,
+ end: null,
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ } );
+
+ it( 'should keep the selection if we are not removing the selected block', () => {
+ const original = deepFreeze( {
+ start: 'chicken',
+ end: 'chicken',
+ initialPosition: null,
+ isMultiSelecting: false,
+ } );
+ const state = blockSelection( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'ribs' ],
+ } );
+
+ expect( state ).toBe( original );
+ } );
+ } );
+
+ describe( 'preferences()', () => {
+ it( 'should apply all defaults', () => {
+ const state = preferences( undefined, {} );
+
+ expect( state ).toEqual( {
+ insertUsage: {},
+ } );
+ } );
+ it( 'should record recently used blocks', () => {
+ const state = preferences( deepFreeze( { insertUsage: {} } ), {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'bacon',
+ name: 'core-embed/twitter',
+ } ],
+ time: 123456,
+ } );
+
+ expect( state ).toEqual( {
+ insertUsage: {
+ 'core-embed/twitter': {
+ time: 123456,
+ count: 1,
+ insert: { name: 'core-embed/twitter' },
+ },
+ },
+ } );
+
+ const twoRecentBlocks = preferences( deepFreeze( {
+ insertUsage: {
+ 'core-embed/twitter': {
+ time: 123456,
+ count: 1,
+ insert: { name: 'core-embed/twitter' },
+ },
+ },
+ } ), {
+ type: 'INSERT_BLOCKS',
+ blocks: [ {
+ clientId: 'eggs',
+ name: 'core-embed/twitter',
+ }, {
+ clientId: 'bacon',
+ name: 'core/block',
+ attributes: { ref: 123 },
+ } ],
+ time: 123457,
+ } );
+
+ expect( twoRecentBlocks ).toEqual( {
+ insertUsage: {
+ 'core-embed/twitter': {
+ time: 123457,
+ count: 2,
+ insert: { name: 'core-embed/twitter' },
+ },
+ 'core/block/123': {
+ time: 123457,
+ count: 1,
+ insert: { name: 'core/block', ref: 123 },
+ },
+ },
+ } );
+ } );
+ } );
+
+ describe( 'blocksMode', () => {
+ it( 'should set mode to html if not set', () => {
+ const action = {
+ type: 'TOGGLE_BLOCK_MODE',
+ clientId: 'chicken',
+ };
+ const value = blocksMode( deepFreeze( {} ), action );
+
+ expect( value ).toEqual( { chicken: 'html' } );
+ } );
+
+ it( 'should toggle mode to visual if set as html', () => {
+ const action = {
+ type: 'TOGGLE_BLOCK_MODE',
+ clientId: 'chicken',
+ };
+ const value = blocksMode( deepFreeze( { chicken: 'html' } ), action );
+
+ expect( value ).toEqual( { chicken: 'visual' } );
+ } );
+ } );
+
+ describe( 'template', () => {
+ it( 'should default to visible', () => {
+ const state = template( undefined, {} );
+
+ expect( state ).toEqual( { isValid: true } );
+ } );
+
+ it( 'should reset the validity flag', () => {
+ const original = deepFreeze( { isValid: false, template: [] } );
+ const state = template( original, {
+ type: 'SET_TEMPLATE_VALIDITY',
+ isValid: true,
+ } );
+
+ expect( state ).toEqual( { isValid: true, template: [] } );
+ } );
+ } );
+
+ describe( 'blockListSettings', () => {
+ it( 'should add new settings', () => {
+ const original = deepFreeze( {} );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ settings: {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ expect( state ).toEqual( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+ } );
+
+ it( 'should return same reference if updated as the same', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ settings: {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should return same reference if updated settings not assigned and id not exists', () => {
+ const original = deepFreeze( {} );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ } );
+
+ expect( state ).toBe( original );
+ } );
+
+ it( 'should update the settings of a block', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ settings: {
+ allowedBlocks: [ 'core/list' ],
+ },
+ } );
+
+ expect( state ).toEqual( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/list' ],
+ },
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+ } );
+
+ it( 'should remove existing settings if updated settings not assigned', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'UPDATE_BLOCK_LIST_SETTINGS',
+ clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ } );
+
+ expect( state ).toEqual( {} );
+ } );
+
+ it( 'should remove the settings of a block when it is replaced', () => {
+ const original = deepFreeze( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'REPLACE_BLOCKS',
+ clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
+ } );
+
+ expect( state ).toEqual( {
+ '9db792c6-a25a-495d-adbd-97d56a4c4189': {
+ allowedBlocks: [ 'core/paragraph' ],
+ },
+ } );
+ } );
+
+ it( 'should remove the settings of a block when it is removed', () => {
+ const original = deepFreeze( {
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ allowedBlocks: true,
+ },
+ } );
+
+ const state = blockListSettings( original, {
+ type: 'REMOVE_BLOCKS',
+ clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
+ } );
+
+ expect( state ).toEqual( {} );
+ } );
+ } );
+} );
diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js
new file mode 100644
index 0000000000000..bcda13ad755ff
--- /dev/null
+++ b/packages/block-editor/src/store/test/selectors.js
@@ -0,0 +1,2321 @@
+/**
+ * External dependencies
+ */
+import { filter } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ registerBlockType,
+ unregisterBlockType,
+ setFreeformContentHandlerName,
+} from '@wordpress/blocks';
+import { RawHTML } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import * as selectors from '../selectors';
+
+const {
+ getBlockDependantsCacheBust,
+ getBlockName,
+ getBlock,
+ getBlocks,
+ getBlockCount,
+ getClientIdsWithDescendants,
+ getClientIdsOfDescendants,
+ hasSelectedBlock,
+ getSelectedBlock,
+ getSelectedBlockClientId,
+ getBlockRootClientId,
+ getBlockHierarchyRootClientId,
+ getGlobalBlockCount,
+ getMultiSelectedBlockClientIds,
+ getMultiSelectedBlocks,
+ getMultiSelectedBlocksStartClientId,
+ getMultiSelectedBlocksEndClientId,
+ getBlockOrder,
+ getBlockIndex,
+ getPreviousBlockClientId,
+ getNextBlockClientId,
+ isBlockSelected,
+ hasSelectedInnerBlock,
+ isBlockWithinSelection,
+ hasMultiSelection,
+ isBlockMultiSelected,
+ isFirstMultiSelectedBlock,
+ getBlockMode,
+ isTyping,
+ isCaretWithinFormattedText,
+ getBlockInsertionPoint,
+ isBlockInsertionPointVisible,
+ isSelectionEnabled,
+ canInsertBlockType,
+ getInserterItems,
+ isValidTemplate,
+ getTemplate,
+ getTemplateLock,
+ getBlockListSettings,
+ INSERTER_UTILITY_HIGH,
+ INSERTER_UTILITY_MEDIUM,
+ INSERTER_UTILITY_LOW,
+} = selectors;
+
+describe( 'selectors', () => {
+ let cachedSelectors;
+
+ beforeAll( () => {
+ cachedSelectors = filter( selectors, ( selector ) => selector.clear );
+ } );
+
+ beforeEach( () => {
+ registerBlockType( 'core/block', {
+ save: () => null,
+ category: 'reusable',
+ title: 'Reusable Block Stub',
+ supports: {
+ inserter: false,
+ },
+ } );
+
+ registerBlockType( 'core/test-block-a', {
+ save: ( props ) => props.attributes.text,
+ category: 'formatting',
+ title: 'Test Block A',
+ icon: 'test',
+ keywords: [ 'testing' ],
+ } );
+
+ registerBlockType( 'core/test-block-b', {
+ save: ( props ) => props.attributes.text,
+ category: 'common',
+ title: 'Test Block B',
+ icon: 'test',
+ keywords: [ 'testing' ],
+ supports: {
+ multiple: false,
+ },
+ } );
+
+ registerBlockType( 'core/test-block-c', {
+ save: ( props ) => props.attributes.text,
+ category: 'common',
+ title: 'Test Block C',
+ icon: 'test',
+ keywords: [ 'testing' ],
+ parent: [ 'core/test-block-b' ],
+ } );
+
+ registerBlockType( 'core/test-freeform', {
+ save: ( props ) => { props.attributes.content } ,
+ category: 'common',
+ title: 'Test Freeform Content Handler',
+ icon: 'test',
+ attributes: {
+ content: {
+ type: 'string',
+ },
+ },
+ } );
+
+ setFreeformContentHandlerName( 'core/test-freeform' );
+
+ cachedSelectors.forEach( ( { clear } ) => clear() );
+ } );
+
+ afterEach( () => {
+ unregisterBlockType( 'core/block' );
+ unregisterBlockType( 'core/test-block-a' );
+ unregisterBlockType( 'core/test-block-b' );
+ unregisterBlockType( 'core/test-block-c' );
+ unregisterBlockType( 'core/test-freeform' );
+
+ setFreeformContentHandlerName( undefined );
+ } );
+
+ describe( 'getBlockDependantsCacheBust', () => {
+ const rootBlock = { clientId: 123, name: 'core/paragraph' };
+ const rootBlockAttributes = {};
+ const rootOrder = [ 123 ];
+
+ it( 'returns an unchanging reference', () => {
+ const rootBlockOrder = [];
+
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ },
+ },
+ };
+
+ const nextState = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ },
+ },
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns a new reference on added inner block', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: [],
+ },
+ },
+ };
+
+ const nextState = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: {},
+ },
+ order: {
+ '': rootOrder,
+ 123: [ 456 ],
+ 456: [],
+ },
+ },
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns an unchanging reference on unchanging inner block', () => {
+ const rootBlockOrder = [ 456 ];
+ const childBlock = { clientId: 456, name: 'core/paragraph' };
+ const childBlockAttributes = {};
+ const childBlockOrder = [];
+
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ };
+
+ const nextState = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns a new reference on updated inner block', () => {
+ const rootBlockOrder = [ 456 ];
+ const childBlockOrder = [];
+
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: {},
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ };
+
+ const nextState = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: { content: [ 'foo' ] },
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ },
+ },
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+
+ it( 'returns a new reference on updated grandchild inner block', () => {
+ const rootBlockOrder = [ 456 ];
+ const childBlock = { clientId: 456, name: 'core/paragraph' };
+ const childBlockAttributes = {};
+ const childBlockOrder = [ 789 ];
+ const grandChildBlockOrder = [];
+
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ 789: {},
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ 789: grandChildBlockOrder,
+ },
+ },
+ };
+
+ const nextState = {
+ blocks: {
+ byClientId: {
+ 123: rootBlock,
+ 456: childBlock,
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: rootBlockAttributes,
+ 456: childBlockAttributes,
+ 789: { content: [ 'foo' ] },
+ },
+ order: {
+ '': rootOrder,
+ 123: rootBlockOrder,
+ 456: childBlockOrder,
+ 789: grandChildBlockOrder,
+ },
+ },
+ };
+
+ expect(
+ getBlockDependantsCacheBust( state, 123 )
+ ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ } );
+ } );
+
+ describe( 'getBlockName', () => {
+ it( 'returns null if no block by clientId', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ };
+
+ const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
+
+ expect( name ).toBe( null );
+ } );
+
+ it( 'returns block name', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
+ clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ name: 'core/paragraph',
+ },
+ },
+ attributes: {
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {},
+ },
+ order: {
+ '': [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
+ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': [],
+ },
+ },
+ };
+
+ const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
+
+ expect( name ).toBe( 'core/paragraph' );
+ } );
+ } );
+
+ describe( 'getBlock', () => {
+ it( 'should return the block', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [],
+ },
+ },
+ };
+
+ expect( getBlock( state, 123 ) ).toEqual( {
+ clientId: 123,
+ name: 'core/paragraph',
+ attributes: {},
+ innerBlocks: [],
+ } );
+ } );
+
+ it( 'should return null if the block is not present in state', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ };
+
+ expect( getBlock( state, 123 ) ).toBe( null );
+ } );
+
+ it( 'should include inner blocks', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/paragraph' },
+ 456: { clientId: 456, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ 456: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [ 456 ],
+ 456: [],
+ },
+ },
+ };
+
+ expect( getBlock( state, 123 ) ).toEqual( {
+ clientId: 123,
+ name: 'core/paragraph',
+ attributes: {},
+ innerBlocks: [ {
+ clientId: 456,
+ name: 'core/paragraph',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
+ } );
+
+ it( 'should merge meta attributes for the block', () => {
+ registerBlockType( 'core/meta-block', {
+ save: ( props ) => props.attributes.text,
+ category: 'common',
+ title: 'test block',
+ attributes: {
+ foo: {
+ type: 'string',
+ source: 'meta',
+ meta: 'foo',
+ },
+ },
+ } );
+
+ const state = {
+ settings: {
+ __experimentalMetaSource: {
+ value: {
+ foo: 'bar',
+ },
+ },
+ },
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/meta-block' },
+ },
+ attributes: {
+ 123: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [],
+ },
+ },
+ };
+
+ expect( getBlock( state, 123 ) ).toEqual( {
+ clientId: 123,
+ name: 'core/meta-block',
+ attributes: {
+ foo: 'bar',
+ },
+ innerBlocks: [],
+ } );
+
+ unregisterBlockType( 'core/meta-block' );
+ } );
+ } );
+
+ describe( 'getBlocks', () => {
+ it( 'should return the ordered blocks', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getBlocks( state ) ).toEqual( [
+ { clientId: 123, name: 'core/paragraph', attributes: {}, innerBlocks: [] },
+ { clientId: 23, name: 'core/heading', attributes: {}, innerBlocks: [] },
+ ] );
+ } );
+ } );
+
+ describe( 'getClientIdsOfDescendants', () => {
+ it( 'should return the ids of any descendants, given an array of clientIds', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
+ 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
+ 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
+ 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
+ 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
+ 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
+ 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
+ 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
+ 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
+ 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
+ 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
+ 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
+ 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
+ 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
+ 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
+ },
+ attributes: {
+ 'uuid-2': {},
+ 'uuid-4': {},
+ 'uuid-6': {},
+ 'uuid-8': {},
+ 'uuid-10': {},
+ 'uuid-12': {},
+ 'uuid-14': {},
+ 'uuid-16': {},
+ 'uuid-18': {},
+ 'uuid-20': {},
+ 'uuid-22': {},
+ 'uuid-24': {},
+ 'uuid-26': {},
+ 'uuid-28': {},
+ 'uuid-30': {},
+ },
+ order: {
+ '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
+ 'uuid-2': [ ],
+ 'uuid-4': [ ],
+ 'uuid-6': [ ],
+ 'uuid-8': [ ],
+ 'uuid-10': [ 'uuid-12', 'uuid-14' ],
+ 'uuid-12': [ 'uuid-16' ],
+ 'uuid-14': [ 'uuid-18' ],
+ 'uuid-16': [ ],
+ 'uuid-18': [ 'uuid-24' ],
+ 'uuid-20': [ ],
+ 'uuid-22': [ ],
+ 'uuid-24': [ 'uuid-26', 'uuid-28' ],
+ 'uuid-26': [ ],
+ 'uuid-28': [ 'uuid-30' ],
+ },
+ },
+ };
+ expect( getClientIdsOfDescendants( state, [ 'uuid-10' ] ) ).toEqual( [
+ 'uuid-12',
+ 'uuid-14',
+ 'uuid-16',
+ 'uuid-18',
+ 'uuid-24',
+ 'uuid-26',
+ 'uuid-28',
+ 'uuid-30',
+ ] );
+ } );
+ } );
+
+ describe( 'getClientIdsWithDescendants', () => {
+ it( 'should return the ids for top-level blocks and their descendants of any depth (for nested blocks).', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
+ 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
+ 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
+ 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
+ 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
+ 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
+ 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
+ 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
+ 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
+ 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
+ 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
+ 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
+ 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
+ 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
+ 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
+ },
+ attributes: {
+ 'uuid-2': {},
+ 'uuid-4': {},
+ 'uuid-6': {},
+ 'uuid-8': {},
+ 'uuid-10': {},
+ 'uuid-12': {},
+ 'uuid-14': {},
+ 'uuid-16': {},
+ 'uuid-18': {},
+ 'uuid-20': {},
+ 'uuid-22': {},
+ 'uuid-24': {},
+ 'uuid-26': {},
+ 'uuid-28': {},
+ 'uuid-30': {},
+ },
+ order: {
+ '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
+ 'uuid-2': [ ],
+ 'uuid-4': [ ],
+ 'uuid-6': [ ],
+ 'uuid-8': [ ],
+ 'uuid-10': [ 'uuid-12', 'uuid-14' ],
+ 'uuid-12': [ 'uuid-16' ],
+ 'uuid-14': [ 'uuid-18' ],
+ 'uuid-16': [ ],
+ 'uuid-18': [ 'uuid-24' ],
+ 'uuid-20': [ ],
+ 'uuid-22': [ ],
+ 'uuid-24': [ 'uuid-26', 'uuid-28' ],
+ 'uuid-26': [ ],
+ 'uuid-28': [ 'uuid-30' ],
+ },
+ },
+ };
+ expect( getClientIdsWithDescendants( state ) ).toEqual( [
+ 'uuid-6',
+ 'uuid-8',
+ 'uuid-10',
+ 'uuid-22',
+ 'uuid-12',
+ 'uuid-14',
+ 'uuid-16',
+ 'uuid-18',
+ 'uuid-24',
+ 'uuid-26',
+ 'uuid-28',
+ 'uuid-30',
+ ] );
+ } );
+ } );
+
+ describe( 'getBlockCount', () => {
+ it( 'should return the number of top-level blocks in the post', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getBlockCount( state ) ).toBe( 2 );
+ } );
+
+ it( 'should return the number of blocks in a nested context', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/columns' },
+ 456: { clientId: 456, name: 'core/paragraph' },
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ 456: {},
+ 789: {},
+ },
+ order: {
+ '': [ 123 ],
+ 123: [ 456, 789 ],
+ },
+ },
+ };
+
+ expect( getBlockCount( state, '123' ) ).toBe( 2 );
+ } );
+ } );
+
+ describe( 'hasSelectedBlock', () => {
+ it( 'should return false if no selection', () => {
+ const state = {
+ blockSelection: {
+ start: null,
+ end: null,
+ },
+ };
+
+ expect( hasSelectedBlock( state ) ).toBe( false );
+ } );
+
+ it( 'should return false if multi-selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ },
+ };
+
+ expect( hasSelectedBlock( state ) ).toBe( false );
+ } );
+
+ it( 'should return true if singular selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ },
+ };
+
+ expect( hasSelectedBlock( state ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'getGlobalBlockCount', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 123: { clientId: 123, name: 'core/heading' },
+ 456: { clientId: 456, name: 'core/paragraph' },
+ 789: { clientId: 789, name: 'core/paragraph' },
+ },
+ attributes: {
+ 123: {},
+ 456: {},
+ 789: {},
+ },
+ order: {
+ '': [ 123, 456 ],
+ },
+ },
+ };
+
+ it( 'should return the global number of blocks in the post', () => {
+ expect( getGlobalBlockCount( state ) ).toBe( 2 );
+ } );
+
+ it( 'should return the global number of blocks in the post of a given type', () => {
+ expect( getGlobalBlockCount( state, 'core/paragraph' ) ).toBe( 1 );
+ } );
+
+ it( 'should return 0 if no blocks exist', () => {
+ const emptyState = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ };
+ expect( getGlobalBlockCount( emptyState ) ).toBe( 0 );
+ expect( getGlobalBlockCount( emptyState, 'core/heading' ) ).toBe( 0 );
+ } );
+ } );
+
+ describe( 'getSelectedBlockClientId', () => {
+ it( 'should return null if no block is selected', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getSelectedBlockClientId( state ) ).toBe( null );
+ } );
+
+ it( 'should return null if there is multi selection', () => {
+ const state = {
+ blockSelection: { start: 23, end: 123 },
+ };
+
+ expect( getSelectedBlockClientId( state ) ).toBe( null );
+ } );
+
+ it( 'should return the selected block ClientId', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 23: {
+ name: 'fake block',
+ },
+ },
+ },
+ blockSelection: { start: 23, end: 23 },
+ };
+
+ expect( getSelectedBlockClientId( state ) ).toEqual( 23 );
+ } );
+ } );
+
+ describe( 'getSelectedBlock', () => {
+ it( 'should return null if no block is selected', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 23, 123 ],
+ 23: [],
+ 123: [],
+ },
+ },
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getSelectedBlock( state ) ).toBe( null );
+ } );
+
+ it( 'should return null if there is multi selection', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 23, 123 ],
+ 23: [],
+ 123: [],
+ },
+ },
+ blockSelection: { start: 23, end: 123 },
+ };
+
+ expect( getSelectedBlock( state ) ).toBe( null );
+ } );
+
+ it( 'should return the selected block', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ 23: { clientId: 23, name: 'core/heading' },
+ 123: { clientId: 123, name: 'core/paragraph' },
+ },
+ attributes: {
+ 23: {},
+ 123: {},
+ },
+ order: {
+ '': [ 23, 123 ],
+ 23: [],
+ 123: [],
+ },
+ },
+ blockSelection: { start: 23, end: 23 },
+ };
+
+ expect( getSelectedBlock( state ) ).toEqual( {
+ clientId: 23,
+ name: 'core/heading',
+ attributes: {},
+ innerBlocks: [],
+ } );
+ } );
+ } );
+
+ describe( 'getBlockRootClientId', () => {
+ it( 'should return null if the block does not exist', () => {
+ const state = {
+ blocks: {
+ order: {},
+ },
+ };
+
+ expect( getBlockRootClientId( state, 56 ) ).toBeNull();
+ } );
+
+ it( 'should return root ClientId relative the block ClientId', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ };
+
+ expect( getBlockRootClientId( state, 56 ) ).toBe( '123' );
+ } );
+ } );
+
+ describe( 'getBlockHierarchyRootClientId', () => {
+ it( 'should return the given block if the block has no parents', () => {
+ const state = {
+ blocks: {
+ order: {},
+ },
+ };
+
+ expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( 56 );
+ } );
+
+ it( 'should return root ClientId relative the block ClientId', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ };
+
+ expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( '123' );
+ } );
+
+ it( 'should return the top level root ClientId relative the block ClientId', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ '123', '23' ],
+ 123: [ '456', '56' ],
+ 56: [ '12' ],
+ },
+ },
+ };
+
+ expect( getBlockHierarchyRootClientId( state, '12' ) ).toBe( '123' );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlockClientIds', () => {
+ it( 'should return empty if there is no multi selection', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [] );
+ } );
+
+ it( 'should return selected block clientIds if there is multi selection', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 4, 3, 2 ] );
+ } );
+
+ it( 'should return selected block clientIds if there is multi selection (nested context)', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ 4: [ 9, 8, 7, 6 ],
+ },
+ },
+ blockSelection: { start: 7, end: 9 },
+ };
+
+ expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 9, 8, 7 ] );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlocks', () => {
+ it( 'should return the same reference on subsequent invocations of empty selection', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ blockSelection: { start: null, end: null },
+ };
+
+ expect(
+ getMultiSelectedBlocks( state )
+ ).toBe( getMultiSelectedBlocks( state ) );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlocksStartClientId', () => {
+ it( 'returns null if there is no multi selection', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getMultiSelectedBlocksStartClientId( state ) ).toBeNull();
+ } );
+
+ it( 'returns multi selection start', () => {
+ const state = {
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ expect( getMultiSelectedBlocksStartClientId( state ) ).toBe( 2 );
+ } );
+ } );
+
+ describe( 'getMultiSelectedBlocksEndClientId', () => {
+ it( 'returns null if there is no multi selection', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( getMultiSelectedBlocksEndClientId( state ) ).toBeNull();
+ } );
+
+ it( 'returns multi selection end', () => {
+ const state = {
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ expect( getMultiSelectedBlocksEndClientId( state ) ).toBe( 4 );
+ } );
+ } );
+
+ describe( 'getBlockOrder', () => {
+ it( 'should return the ordered block ClientIds of top-level blocks by default', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getBlockOrder( state ) ).toEqual( [ 123, 23 ] );
+ } );
+
+ it( 'should return the ordered block ClientIds at a specified rootClientId', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456 ],
+ },
+ },
+ };
+
+ expect( getBlockOrder( state, '123' ) ).toEqual( [ 456 ] );
+ } );
+ } );
+
+ describe( 'getBlockIndex', () => {
+ it( 'should return the block order', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getBlockIndex( state, 23 ) ).toBe( 1 );
+ } );
+
+ it( 'should return the block order (nested context)', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ };
+
+ expect( getBlockIndex( state, 56, '123' ) ).toBe( 1 );
+ } );
+ } );
+
+ describe( 'getPreviousBlockClientId', () => {
+ it( 'should return the previous block', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 23 ) ).toEqual( 123 );
+ } );
+
+ it( 'should return the previous block (nested context)', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 56, '123' ) ).toEqual( 456 );
+ } );
+
+ it( 'should return null for the first block', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 123 ) ).toBeNull();
+ } );
+
+ it( 'should return null for the first block (nested context)', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ };
+
+ expect( getPreviousBlockClientId( state, 456, '123' ) ).toBeNull();
+ } );
+ } );
+
+ describe( 'getNextBlockClientId', () => {
+ it( 'should return the following block', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 123 ) ).toEqual( 23 );
+ } );
+
+ it( 'should return the following block (nested context)', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 456, '123' ) ).toEqual( 56 );
+ } );
+
+ it( 'should return null for the last block', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 23 ) ).toBeNull();
+ } );
+
+ it( 'should return null for the last block (nested context)', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 123, 23 ],
+ 123: [ 456, 56 ],
+ },
+ },
+ };
+
+ expect( getNextBlockClientId( state, 56, '123' ) ).toBeNull();
+ } );
+ } );
+
+ describe( 'isBlockSelected', () => {
+ it( 'should return true if the block is selected', () => {
+ const state = {
+ blockSelection: { start: 123, end: 123 },
+ };
+
+ expect( isBlockSelected( state, 123 ) ).toBe( true );
+ } );
+
+ it( 'should return false if a multi-selection range exists', () => {
+ const state = {
+ blockSelection: { start: 123, end: 124 },
+ };
+
+ expect( isBlockSelected( state, 123 ) ).toBe( false );
+ } );
+
+ it( 'should return false if the block is not selected', () => {
+ const state = {
+ blockSelection: { start: null, end: null },
+ };
+
+ expect( isBlockSelected( state, 23 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'hasSelectedInnerBlock', () => {
+ it( 'should return false if the selected block is a child of the given ClientId', () => {
+ const state = {
+ blockSelection: { start: 5, end: 5 },
+ blocks: {
+ order: {
+ 4: [ 3, 2, 1 ],
+ },
+ },
+ };
+
+ expect( hasSelectedInnerBlock( state, 4 ) ).toBe( false );
+ } );
+
+ it( 'should return true if the selected block is a child of the given ClientId', () => {
+ const state = {
+ blockSelection: { start: 3, end: 3 },
+ blocks: {
+ order: {
+ 4: [ 3, 2, 1 ],
+ },
+ },
+ };
+
+ expect( hasSelectedInnerBlock( state, 4 ) ).toBe( true );
+ } );
+
+ it( 'should return true if a multi selection exists that contains children of the block with the given ClientId', () => {
+ const state = {
+ blocks: {
+ order: {
+ 6: [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+ expect( hasSelectedInnerBlock( state, 6 ) ).toBe( true );
+ } );
+
+ it( 'should return false if a multi selection exists bot does not contains children of the block with the given ClientId', () => {
+ const state = {
+ blocks: {
+ order: {
+ 3: [ 2, 1 ],
+ 6: [ 5, 4 ],
+ },
+ },
+ blockSelection: { start: 5, end: 4 },
+ };
+ expect( hasSelectedInnerBlock( state, 3 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isBlockWithinSelection', () => {
+ it( 'should return true if the block is selected but not the last', () => {
+ const state = {
+ blockSelection: { start: 5, end: 3 },
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 4 ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is the last selected', () => {
+ const state = {
+ blockSelection: { start: 5, end: 3 },
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 3 ) ).toBe( false );
+ } );
+
+ it( 'should return false if the block is not selected', () => {
+ const state = {
+ blockSelection: { start: 5, end: 3 },
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 2 ) ).toBe( false );
+ } );
+
+ it( 'should return false if there is no selection', () => {
+ const state = {
+ blockSelection: {},
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ };
+
+ expect( isBlockWithinSelection( state, 4 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'hasMultiSelection', () => {
+ it( 'should return false if no selection', () => {
+ const state = {
+ blockSelection: {
+ start: null,
+ end: null,
+ },
+ };
+
+ expect( hasMultiSelection( state ) ).toBe( false );
+ } );
+
+ it( 'should return false if singular selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ },
+ };
+
+ expect( hasMultiSelection( state ) ).toBe( false );
+ } );
+
+ it( 'should return true if multi-selection', () => {
+ const state = {
+ blockSelection: {
+ start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
+ end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
+ },
+ };
+
+ expect( hasMultiSelection( state ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'isBlockMultiSelected', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ it( 'should return true if the block is multi selected', () => {
+ expect( isBlockMultiSelected( state, 3 ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is not multi selected', () => {
+ expect( isBlockMultiSelected( state, 5 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isFirstMultiSelectedBlock', () => {
+ const state = {
+ blocks: {
+ order: {
+ '': [ 5, 4, 3, 2, 1 ],
+ },
+ },
+ blockSelection: { start: 2, end: 4 },
+ };
+
+ it( 'should return true if the block is first in multi selection', () => {
+ expect( isFirstMultiSelectedBlock( state, 4 ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is not first in multi selection', () => {
+ expect( isFirstMultiSelectedBlock( state, 3 ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'getBlockMode', () => {
+ it( 'should return "visual" if unset', () => {
+ const state = {
+ blocksMode: {},
+ };
+
+ expect( getBlockMode( state, 123 ) ).toEqual( 'visual' );
+ } );
+
+ it( 'should return the block mode', () => {
+ const state = {
+ blocksMode: {
+ 123: 'html',
+ },
+ };
+
+ expect( getBlockMode( state, 123 ) ).toEqual( 'html' );
+ } );
+ } );
+
+ describe( 'isTyping', () => {
+ it( 'should return the isTyping flag if the block is selected', () => {
+ const state = {
+ isTyping: true,
+ };
+
+ expect( isTyping( state ) ).toBe( true );
+ } );
+
+ it( 'should return false if the block is not selected', () => {
+ const state = {
+ isTyping: false,
+ };
+
+ expect( isTyping( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isCaretWithinFormattedText', () => {
+ it( 'returns true if the isCaretWithinFormattedText state is also true', () => {
+ const state = {
+ isCaretWithinFormattedText: true,
+ };
+
+ expect( isCaretWithinFormattedText( state ) ).toBe( true );
+ } );
+
+ it( 'returns false if the isCaretWithinFormattedText state is also false', () => {
+ const state = {
+ isCaretWithinFormattedText: false,
+ };
+
+ expect( isCaretWithinFormattedText( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'isSelectionEnabled', () => {
+ it( 'should return true if selection is enable', () => {
+ const state = {
+ blockSelection: {
+ isEnabled: true,
+ },
+ };
+
+ expect( isSelectionEnabled( state ) ).toBe( true );
+ } );
+
+ it( 'should return false if selection is disabled', () => {
+ const state = {
+ blockSelection: {
+ isEnabled: false,
+ },
+ };
+
+ expect( isSelectionEnabled( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'getBlockInsertionPoint', () => {
+ it( 'should return the explicitly assigned insertion point', () => {
+ const state = {
+ blockSelection: {
+ start: 'clientId2',
+ end: 'clientId2',
+ },
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1' ],
+ clientId1: [ 'clientId2' ],
+ clientId2: [],
+ },
+ },
+ insertionPoint: {
+ rootClientId: undefined,
+ index: 0,
+ },
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 0,
+ } );
+ } );
+
+ it( 'should return an object for the selected block', () => {
+ const state = {
+ blockSelection: {
+ start: 'clientId1',
+ end: 'clientId1',
+ },
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ },
+ attributes: {
+ clientId1: {},
+ },
+ order: {
+ '': [ 'clientId1' ],
+ clientId1: [],
+ },
+ },
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 1,
+ } );
+ } );
+
+ it( 'should return an object for the nested selected block', () => {
+ const state = {
+ blockSelection: {
+ start: 'clientId2',
+ end: 'clientId2',
+ },
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1' ],
+ clientId1: [ 'clientId2' ],
+ clientId2: [],
+ },
+ },
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: 'clientId1',
+ index: 1,
+ } );
+ } );
+
+ it( 'should return an object for the last multi selected clientId', () => {
+ const state = {
+ blockSelection: {
+ start: 'clientId1',
+ end: 'clientId2',
+ },
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1', 'clientId2' ],
+ clientId1: [],
+ clientId2: [],
+ },
+ },
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 2,
+ } );
+ } );
+
+ it( 'should return an object for the last block if no selection', () => {
+ const state = {
+ blockSelection: {
+ start: null,
+ end: null,
+ },
+ blocks: {
+ byClientId: {
+ clientId1: { clientId: 'clientId1' },
+ clientId2: { clientId: 'clientId2' },
+ },
+ attributes: {
+ clientId1: {},
+ clientId2: {},
+ },
+ order: {
+ '': [ 'clientId1', 'clientId2' ],
+ clientId1: [],
+ clientId2: [],
+ },
+ },
+ insertionPoint: null,
+ };
+
+ expect( getBlockInsertionPoint( state ) ).toEqual( {
+ rootClientId: undefined,
+ index: 2,
+ } );
+ } );
+ } );
+
+ describe( 'isBlockInsertionPointVisible', () => {
+ it( 'should return false if no assigned insertion point', () => {
+ const state = {
+ insertionPoint: null,
+ };
+
+ expect( isBlockInsertionPointVisible( state ) ).toBe( false );
+ } );
+
+ it( 'should return true if assigned insertion point', () => {
+ const state = {
+ insertionPoint: {
+ rootClientId: undefined,
+ index: 5,
+ },
+ };
+
+ expect( isBlockInsertionPointVisible( state ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'canInsertBlockType', () => {
+ it( 'should deny blocks that are not registered', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/invalid' ) ).toBe( false );
+ } );
+
+ it( 'should deny blocks that are not allowed by the editor', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ blockListSettings: {},
+ settings: {
+ allowedBlockTypes: [],
+ },
+ };
+ expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
+ } );
+
+ it( 'should allow blocks that are allowed by the editor', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ blockListSettings: {},
+ settings: {
+ allowedBlockTypes: [ 'core/test-block-a' ],
+ },
+ };
+ expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( true );
+ } );
+
+ it( 'should deny blocks when the editor has a template lock', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ blockListSettings: {},
+ settings: {
+ templateLock: 'all',
+ },
+ };
+ expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
+ } );
+
+ it( 'should deny blocks that restrict parent from being inserted into the root', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c' ) ).toBe( false );
+ } );
+
+ it( 'should deny blocks that restrict parent from being inserted into a restricted parent', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( false );
+ } );
+
+ it( 'should allow blocks that restrict parent to be inserted into an allowed parent', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
+ } );
+
+ it( 'should deny restricted blocks from being inserted into a block that restricts allowedBlocks', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ blockListSettings: {
+ block1: {
+ allowedBlocks: [ 'core/test-block-c' ],
+ },
+ },
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( false );
+ } );
+
+ it( 'should allow allowed blocks to be inserted into a block that restricts allowedBlocks', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ blockListSettings: {
+ block1: {
+ allowedBlocks: [ 'core/test-block-b' ],
+ },
+ },
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( true );
+ } );
+
+ it( 'should prioritise parent over allowedBlocks', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: {},
+ },
+ },
+ blockListSettings: {
+ block1: {
+ allowedBlocks: [],
+ },
+ },
+ settings: {},
+ };
+ expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
+ } );
+ } );
+
+ describe( 'getInserterItems', () => {
+ it( 'should properly list block type and reusable block items', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ },
+ order: {},
+ },
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'block1', title: 'Reusable Block 1' },
+ ],
+ },
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+ const items = getInserterItems( state );
+ const testBlockAItem = items.find( ( item ) => item.id === 'core/test-block-a' );
+ expect( testBlockAItem ).toEqual( {
+ id: 'core/test-block-a',
+ name: 'core/test-block-a',
+ initialAttributes: {},
+ title: 'Test Block A',
+ icon: {
+ src: 'test',
+ },
+ category: 'formatting',
+ keywords: [ 'testing' ],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ hasChildBlocksWithInserterSupport: false,
+ } );
+ const reusableBlockItem = items.find( ( item ) => item.id === 'core/block/1' );
+ expect( reusableBlockItem ).toEqual( {
+ id: 'core/block/1',
+ name: 'core/block',
+ initialAttributes: { ref: 1 },
+ title: 'Reusable Block 1',
+ icon: {
+ src: 'test',
+ },
+ category: 'reusable',
+ keywords: [],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ } );
+ } );
+
+ it( 'should not list a reusable block item if it is being inserted inside it self', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1ref: {
+ name: 'core/block',
+ clientId: 'block1ref',
+ },
+ itselfBlock1: { name: 'core/test-block-a' },
+ itselfBlock2: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1ref: {
+ attributes: {
+ ref: 1,
+ },
+ },
+ itselfBlock1: {},
+ itselfBlock2: {},
+ },
+ order: {
+ '': [ 'block1ref' ],
+ },
+ },
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'itselfBlock1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'itselfBlock2', title: 'Reusable Block 2' },
+ ],
+ },
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+ const items = getInserterItems( state, 'itselfBlock1' );
+ const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
+ expect( reusableBlockItems ).toHaveLength( 1 );
+ expect( reusableBlockItems[ 0 ] ).toEqual( {
+ id: 'core/block/2',
+ name: 'core/block',
+ initialAttributes: { ref: 2 },
+ title: 'Reusable Block 2',
+ icon: {
+ src: 'test',
+ },
+ category: 'reusable',
+ keywords: [],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ } );
+ } );
+
+ it( 'should not list a reusable block item if it is being inserted inside a descendent', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block2ref: {
+ name: 'core/block',
+ clientId: 'block1ref',
+ },
+ referredBlock1: { name: 'core/test-block-a' },
+ referredBlock2: { name: 'core/test-block-b' },
+ childReferredBlock2: { name: 'core/test-block-a' },
+ grandchildReferredBlock2: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block2ref: {
+ attributes: {
+ ref: 2,
+ },
+ },
+ referredBlock1: {},
+ referredBlock2: {},
+ childReferredBlock2: {},
+ grandchildReferredBlock2: {},
+ },
+ order: {
+ '': [ 'block2ref' ],
+ referredBlock2: [ 'childReferredBlock2' ],
+ childReferredBlock2: [ 'grandchildReferredBlock2' ],
+ },
+ },
+
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'referredBlock1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'referredBlock2', title: 'Reusable Block 2' },
+ ],
+ },
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+ const items = getInserterItems( state, 'grandchildReferredBlock2' );
+ const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
+ expect( reusableBlockItems ).toHaveLength( 1 );
+ expect( reusableBlockItems[ 0 ] ).toEqual( {
+ id: 'core/block/1',
+ name: 'core/block',
+ initialAttributes: { ref: 1 },
+ title: 'Reusable Block 1',
+ icon: {
+ src: 'test',
+ },
+ category: 'reusable',
+ keywords: [],
+ isDisabled: false,
+ utility: 0,
+ frecency: 0,
+ } );
+ } );
+ it( 'should order items by descending utility and frecency', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ block2: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ block2: {},
+ },
+ order: {},
+ },
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'block1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'block2', title: 'Reusable Block 2' },
+ ],
+ },
+ preferences: {
+ insertUsage: {
+ 'core/block/1': { count: 10, time: 1000 },
+ 'core/block/2': { count: 20, time: 1000 },
+ },
+ },
+ blockListSettings: {},
+ };
+ const itemIDs = getInserterItems( state ).map( ( item ) => item.id );
+ expect( itemIDs ).toEqual( [
+ 'core/block/2',
+ 'core/block/1',
+ 'core/test-block-b',
+ 'core/test-freeform',
+ 'core/test-block-a',
+ ] );
+ } );
+
+ it( 'should correctly cache the return values', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-a' },
+ block2: { name: 'core/test-block-a' },
+ block3: { name: 'core/test-block-a' },
+ block4: { name: 'core/test-block-a' },
+ },
+ attributes: {
+ block1: {},
+ block2: {},
+ block3: {},
+ block4: {},
+ },
+ order: {
+ '': [ 'block3', 'block4' ],
+ },
+ },
+ settings: {
+ __experimentalReusableBlocks: [
+ { id: 1, isTemporary: false, clientId: 'block1', title: 'Reusable Block 1' },
+ { id: 2, isTemporary: false, clientId: 'block2', title: 'Reusable Block 2' },
+ ],
+ },
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ };
+
+ const stateSecondBlockRestricted = {
+ ...state,
+ blockListSettings: {
+ block4: {
+ allowedBlocks: [ 'core/test-block-b' ],
+ },
+ },
+ };
+
+ const firstBlockFirstCall = getInserterItems( state, 'block3' );
+ const firstBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block3' );
+ expect( firstBlockFirstCall ).toBe( firstBlockSecondCall );
+ expect( firstBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
+ 'core/test-block-b',
+ 'core/test-freeform',
+ 'core/test-block-a',
+ 'core/block/1',
+ 'core/block/2',
+ ] );
+
+ const secondBlockFirstCall = getInserterItems( state, 'block4' );
+ const secondBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block4' );
+ expect( secondBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
+ 'core/test-block-b',
+ 'core/test-freeform',
+ 'core/test-block-a',
+ 'core/block/1',
+ 'core/block/2',
+ ] );
+ expect( secondBlockSecondCall.map( ( item ) => item.id ) ).toEqual( [
+ 'core/test-block-b',
+ ] );
+ } );
+
+ it( 'should set isDisabled when a block with `multiple: false` has been used', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { clientId: 'block1', name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: { attribute: {} },
+ },
+ order: {
+ '': [ 'block1' ],
+ },
+ },
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state );
+ const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
+ expect( testBlockBItem.isDisabled ).toBe( true );
+ } );
+
+ it( 'should give common blocks a low utility', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state );
+ const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
+ expect( testBlockBItem.utility ).toBe( INSERTER_UTILITY_LOW );
+ } );
+
+ it( 'should give used blocks a medium utility and set a frecency', () => {
+ const state = {
+ blocks: {
+ byClientId: {},
+ attributes: {},
+ order: {},
+ },
+ preferences: {
+ insertUsage: {
+ 'core/test-block-b': { count: 10, time: 1000 },
+ },
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state );
+ const reusableBlock2Item = items.find( ( item ) => item.id === 'core/test-block-b' );
+ expect( reusableBlock2Item.utility ).toBe( INSERTER_UTILITY_MEDIUM );
+ expect( reusableBlock2Item.frecency ).toBe( 2.5 );
+ } );
+
+ it( 'should give contextual blocks a high utility', () => {
+ const state = {
+ blocks: {
+ byClientId: {
+ block1: { name: 'core/test-block-b' },
+ },
+ attributes: {
+ block1: { attribute: {} },
+ },
+ order: {
+ '': [ 'block1' ],
+ },
+ },
+ preferences: {
+ insertUsage: {},
+ },
+ blockListSettings: {},
+ settings: {},
+ };
+ const items = getInserterItems( state, 'block1' );
+ const testBlockCItem = items.find( ( item ) => item.id === 'core/test-block-c' );
+ expect( testBlockCItem.utility ).toBe( INSERTER_UTILITY_HIGH );
+ } );
+ } );
+
+ describe( 'isValidTemplate', () => {
+ it( 'should return true if template is valid', () => {
+ const state = {
+ template: { isValid: true },
+ };
+
+ expect( isValidTemplate( state ) ).toBe( true );
+ } );
+
+ it( 'should return false if template is not valid', () => {
+ const state = {
+ template: { isValid: false },
+ };
+
+ expect( isValidTemplate( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'getTemplate', () => {
+ it( 'should return the template object', () => {
+ const template = [];
+ const state = {
+ settings: { template },
+ };
+
+ expect( getTemplate( state ) ).toBe( template );
+ } );
+ } );
+
+ describe( 'getTemplateLock', () => {
+ it( 'should return the general template lock if no clientId was set', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ };
+
+ expect( getTemplateLock( state ) ).toBe( 'all' );
+ } );
+
+ it( 'should return null if the specified clientId was not found ', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ blockListSettings: {
+ chicken: {
+ templateLock: 'insert',
+ },
+ },
+ };
+
+ expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
+ } );
+
+ it( 'should return null if template lock was not set on the specified block', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ blockListSettings: {
+ chicken: {
+ test: 'tes1t',
+ },
+ },
+ };
+
+ expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
+ } );
+
+ it( 'should return the template lock for the specified clientId', () => {
+ const state = {
+ settings: { templateLock: 'all' },
+ blockListSettings: {
+ chicken: {
+ templateLock: 'insert',
+ },
+ },
+ };
+
+ expect( getTemplateLock( state, 'chicken' ) ).toBe( 'insert' );
+ } );
+ } );
+
+ describe( 'getBlockListSettings', () => {
+ it( 'should return the settings of a block', () => {
+ const state = {
+ blockListSettings: {
+ chicken: {
+ setting1: false,
+ },
+ ribs: {
+ setting2: true,
+ },
+ },
+ };
+
+ expect( getBlockListSettings( state, 'chicken' ) ).toEqual( {
+ setting1: false,
+ } );
+ } );
+
+ it( 'should return undefined if settings for the block don’t exist', () => {
+ const state = {
+ blockListSettings: {},
+ };
+
+ expect( getBlockListSettings( state, 'chicken' ) ).toBe( undefined );
+ } );
+ } );
+} );
diff --git a/packages/data/src/plugins/persistence/index.js b/packages/data/src/plugins/persistence/index.js
index 4a286e65ca71e..5ce7f41a37c2f 100644
--- a/packages/data/src/plugins/persistence/index.js
+++ b/packages/data/src/plugins/persistence/index.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { flow, merge, isPlainObject } from 'lodash';
+import { flow, merge, isPlainObject, omit } from 'lodash';
/**
* Internal dependencies
@@ -115,7 +115,7 @@ export function createPersistenceInterface( options ) {
*
* @return {WPDataPlugin} Data plugin.
*/
-export default function( registry, pluginOptions ) {
+const persistencePlugin = function( registry, pluginOptions ) {
const persistence = createPersistenceInterface( pluginOptions );
/**
@@ -201,4 +201,31 @@ export default function( registry, pluginOptions ) {
return store;
},
};
-}
+};
+
+/**
+ * Deprecated: Remove this function once WordPress 5.3 is released.
+ */
+
+persistencePlugin.__unstableMigrate = ( pluginOptions ) => {
+ const persistence = createPersistenceInterface( pluginOptions );
+
+ // Preferences migration to introduce the block editor module
+ const persistedState = persistence.get();
+ const coreEditorState = persistedState[ 'core/editor' ];
+ if ( coreEditorState && coreEditorState.preferences && coreEditorState.preferences.insertUsage ) {
+ const blockEditorState = {
+ preferences: {
+ insertUsage: coreEditorState.preferences.insertUsage,
+ },
+ };
+
+ persistence.set( 'core/editor', {
+ ...coreEditorState,
+ preferences: omit( coreEditorState.preferences, [ 'insertUsage' ] ),
+ } );
+ persistence.set( 'core/block-editor', blockEditorState );
+ }
+};
+
+export default persistencePlugin;
diff --git a/packages/e2e-tests/specs/blocks/preformatted.test.js b/packages/e2e-tests/specs/blocks/preformatted.test.js
index 3397c9f75f68d..a5f9965af3c51 100644
--- a/packages/e2e-tests/specs/blocks/preformatted.test.js
+++ b/packages/e2e-tests/specs/blocks/preformatted.test.js
@@ -22,11 +22,14 @@ describe( 'Preformatted', () => {
expect( await getEditedPostContent() ).toMatchSnapshot();
await page.keyboard.press( 'Escape' );
+ await page.waitForSelector( 'button[aria-label="More options"]' );
await page.click( 'button[aria-label="More options"]' );
await clickButton( 'Convert to Blocks' );
// Once it's edited, it should be saved as BR tags.
await page.keyboard.type( '0' );
await page.keyboard.press( 'Enter' );
+ await page.keyboard.press( 'Escape' );
+ await page.waitForSelector( 'button[aria-label="More options"]' );
await page.click( 'button[aria-label="More options"]' );
await clickButton( 'Edit as HTML' );
diff --git a/packages/e2e-tests/specs/plugins/container-blocks.test.js b/packages/e2e-tests/specs/plugins/container-blocks.test.js
index 2d32224bb3503..50c4d80854e74 100644
--- a/packages/e2e-tests/specs/plugins/container-blocks.test.js
+++ b/packages/e2e-tests/specs/plugins/container-blocks.test.js
@@ -31,6 +31,7 @@ describe( 'InnerBlocks Template Sync', () => {
`;
await insertBlock( blockName );
await switchEditorModeTo( 'Code' );
+ await page.waitForSelector( '.editor-post-text-editor' );
await page.$eval( '.editor-post-text-editor', ( element, _paragraph, _blockSlug ) => {
const blockDelimiter = ``;
element.value = element.value.replace( blockDelimiter, `${ _paragraph }${ blockDelimiter }` );
diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js
index 74e035fb2fd52..3e51956a0e074 100644
--- a/packages/edit-post/src/editor.js
+++ b/packages/edit-post/src/editor.js
@@ -1,9 +1,14 @@
+/**
+ * External dependencies
+ */
+import memize from 'memize';
+
/**
* WordPress dependencies
*/
import { withSelect } from '@wordpress/data';
import { EditorProvider, ErrorBoundary, PostLockedModal } from '@wordpress/editor';
-import { StrictMode } from '@wordpress/element';
+import { StrictMode, Component } from '@wordpress/element';
import { KeyboardShortcuts } from '@wordpress/components';
/**
@@ -12,41 +17,61 @@ import { KeyboardShortcuts } from '@wordpress/components';
import preventEventDiscovery from './prevent-event-discovery';
import Layout from './components/layout';
-function Editor( {
- settings,
- hasFixedToolbar,
- focusMode,
- post,
- initialEdits,
- onError,
- ...props
-} ) {
- if ( ! post ) {
- return null;
+class Editor extends Component {
+ constructor() {
+ super( ...arguments );
+
+ this.getEditorSettings = memize( this.getEditorSettings, {
+ maxSize: 1,
+ } );
+ }
+
+ getEditorSettings( settings, hasFixedToolbar, focusMode ) {
+ return {
+ ...settings,
+ hasFixedToolbar,
+ focusMode,
+ };
}
- const editorSettings = {
- ...settings,
- hasFixedToolbar,
- focusMode,
- };
-
- return (
-
-
-
-
-
-
-
-
-
- );
+ render() {
+ const {
+ settings,
+ hasFixedToolbar,
+ focusMode,
+ post,
+ initialEdits,
+ onError,
+ ...props
+ } = this.props;
+
+ if ( ! post ) {
+ return null;
+ }
+
+ const editorSettings = {
+ ...settings,
+ hasFixedToolbar,
+ focusMode,
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
}
export default withSelect( ( select, { postId, postType } ) => ( {
diff --git a/packages/editor/package.json b/packages/editor/package.json
index c82d754733f61..acbb00bec5532 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -24,6 +24,7 @@
"@wordpress/a11y": "file:../a11y",
"@wordpress/api-fetch": "file:../api-fetch",
"@wordpress/blob": "file:../blob",
+ "@wordpress/block-editor": "file:../block-editor",
"@wordpress/blocks": "file:../blocks",
"@wordpress/components": "file:../components",
"@wordpress/compose": "file:../compose",
diff --git a/packages/editor/src/components/block-list/block.js b/packages/editor/src/components/block-list/block.js
index fc79575258011..22b037c7e2e52 100644
--- a/packages/editor/src/components/block-list/block.js
+++ b/packages/editor/src/components/block-list/block.js
@@ -693,7 +693,6 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
removeBlock,
mergeBlocks,
replaceBlocks,
- editPost,
toggleSelection,
} = dispatch( 'core/editor' );
@@ -749,8 +748,10 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
onReplace( blocks ) {
replaceBlocks( [ ownProps.clientId ], blocks );
},
- onMetaChange( meta ) {
- editPost( { meta } );
+ onMetaChange( updatedMeta ) {
+ const { getEditorSettings } = select( 'core/editor' );
+ const onChangeMeta = getEditorSettings().__experimentalMetaSource.onChange;
+ onChangeMeta( updatedMeta );
},
onShiftSelection() {
if ( ! ownProps.isSelectionEnabled ) {
diff --git a/packages/editor/src/components/post-text-editor/index.js b/packages/editor/src/components/post-text-editor/index.js
index c08e59f35a400..13926f4b79e85 100644
--- a/packages/editor/src/components/post-text-editor/index.js
+++ b/packages/editor/src/components/post-text-editor/index.js
@@ -65,7 +65,6 @@ export class PostTextEditor extends Component {
render() {
const { value } = this.state;
const { instanceId } = this.props;
-
return (
@@ -94,13 +93,14 @@ export default compose( [
};
} ),
withDispatch( ( dispatch ) => {
- const { editPost, resetBlocks } = dispatch( 'core/editor' );
+ const { editPost, resetEditorBlocks } = dispatch( 'core/editor' );
return {
onChange( content ) {
editPost( { content } );
},
onPersist( content ) {
- resetBlocks( parse( content ) );
+ const blocks = parse( content );
+ resetEditorBlocks( blocks );
},
};
} ),
diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js
index f22f129cc8acf..62bf481698b0a 100644
--- a/packages/editor/src/components/provider/index.js
+++ b/packages/editor/src/components/provider/index.js
@@ -1,33 +1,37 @@
/**
* External dependencies
*/
-import { flow, map } from 'lodash';
+import { map } from 'lodash';
+import memize from 'memize';
/**
* WordPress dependencies
*/
-import { createElement, Component } from '@wordpress/element';
-import { DropZoneProvider, SlotFillProvider } from '@wordpress/components';
-import { withDispatch } from '@wordpress/data';
+import { compose } from '@wordpress/compose';
+import { Component } from '@wordpress/element';
+import { withDispatch, withSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
+import { BlockEditorProvider } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import transformStyles from '../../editor-styles';
-
class EditorProvider extends Component {
constructor( props ) {
super( ...arguments );
+ this.getBlockEditorSettings = memize( this.getBlockEditorSettings, {
+ maxSize: 1,
+ } );
+
// Assume that we don't need to initialize in the case of an error recovery.
if ( props.recovery ) {
return;
}
- props.updateEditorSettings( props.settings );
props.updatePostLock( props.settings.postLock );
- props.setupEditor( props.post, props.initialEdits );
+ props.setupEditor( props.post, props.initialEdits, props.settings.template );
if ( props.settings.autosave ) {
props.createWarningNotice(
@@ -45,6 +49,17 @@ class EditorProvider extends Component {
}
}
+ getBlockEditorSettings( settings, meta, onMetaChange, reusableBlocks ) {
+ return {
+ ...settings,
+ __experimentalMetaSource: {
+ value: meta,
+ onChange: onMetaChange,
+ },
+ __experimentalReusableBlocks: reusableBlocks,
+ };
+ }
+
componentDidMount() {
if ( ! this.props.settings.styles ) {
return;
@@ -61,55 +76,77 @@ class EditorProvider extends Component {
} );
}
- componentDidUpdate( prevProps ) {
- if ( this.props.settings !== prevProps.settings ) {
- this.props.updateEditorSettings( this.props.settings );
- }
- }
-
render() {
const {
children,
+ blocks,
+ resetEditorBlocks,
+ isReady,
+ settings,
+ meta,
+ onMetaChange,
+ reusableBlocks,
+ resetEditorBlocksWithoutUndoLevel,
} = this.props;
- const providers = [
- // Slot / Fill provider:
- //
- // - context.getSlot
- // - context.registerSlot
- // - context.unregisterSlot
- [
- SlotFillProvider,
- ],
-
- // DropZone provider:
- [
- DropZoneProvider,
- ],
- ];
-
- const createEditorElement = flow(
- providers.map( ( [ Provider, props ] ) => (
- ( arg ) => createElement( Provider, props, arg )
- ) )
+ if ( ! isReady ) {
+ return null;
+ }
+
+ const editorSettings = this.getBlockEditorSettings(
+ settings, meta, onMetaChange, reusableBlocks
);
- return createEditorElement( children );
+ return (
+
+ { children }
+
+ );
}
}
-export default withDispatch( ( dispatch ) => {
- const {
- setupEditor,
- updateEditorSettings,
- updatePostLock,
- } = dispatch( 'core/editor' );
- const { createWarningNotice } = dispatch( 'core/notices' );
-
- return {
- setupEditor,
- updateEditorSettings,
- updatePostLock,
- createWarningNotice,
- };
-} )( EditorProvider );
+export default compose( [
+ withSelect( ( select ) => {
+ const {
+ __unstableIsEditorReady: isEditorReady,
+ getEditorBlocks,
+ getEditedPostAttribute,
+ __experimentalGetReusableBlocks,
+ } = select( 'core/editor' );
+ return {
+ isReady: isEditorReady(),
+ blocks: getEditorBlocks(),
+ meta: getEditedPostAttribute( 'meta' ),
+ reusableBlocks: __experimentalGetReusableBlocks(),
+ };
+ } ),
+ withDispatch( ( dispatch ) => {
+ const {
+ setupEditor,
+ updatePostLock,
+ resetEditorBlocks,
+ editPost,
+ } = dispatch( 'core/editor' );
+ const { createWarningNotice } = dispatch( 'core/notices' );
+
+ return {
+ setupEditor,
+ updatePostLock,
+ createWarningNotice,
+ resetEditorBlocks,
+ resetEditorBlocksWithoutUndoLevel( blocks ) {
+ resetEditorBlocks( blocks, {
+ __unstableShouldCreateUndoLevel: false,
+ } );
+ },
+ onMetaChange( meta ) {
+ editPost( { meta } );
+ },
+ };
+ } ),
+] )( EditorProvider );
diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js
index 724c9f26e30e6..30cc785d63448 100644
--- a/packages/editor/src/components/rich-text/index.js
+++ b/packages/editor/src/components/rich-text/index.js
@@ -1102,17 +1102,13 @@ const RichTextContainer = compose( [
} ),
withDispatch( ( dispatch ) => {
const {
- createUndoLevel,
- redo,
- undo,
+ __unstableMarkLastChangeAsPersistent,
enterFormattedText,
exitFormattedText,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
return {
- onCreateUndoLevel: createUndoLevel,
- onRedo: redo,
- onUndo: undo,
+ onCreateUndoLevel: __unstableMarkLastChangeAsPersistent,
onEnterFormattedText: enterFormattedText,
onExitFormattedText: exitFormattedText,
};
diff --git a/packages/editor/src/index.js b/packages/editor/src/index.js
index 7a88f75dbccb5..1f5baad7285f2 100644
--- a/packages/editor/src/index.js
+++ b/packages/editor/src/index.js
@@ -1,6 +1,7 @@
/**
* WordPress dependencies
*/
+import '@wordpress/block-editor';
import '@wordpress/blocks';
import '@wordpress/core-data';
import '@wordpress/notices';
diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js
index b65774fe4c219..0d508375c37c8 100644
--- a/packages/editor/src/store/actions.js
+++ b/packages/editor/src/store/actions.js
@@ -3,30 +3,27 @@
*/
import { castArray } from 'lodash';
-/**
- * WordPress dependencies
- */
-import { getDefaultBlockName, createBlock } from '@wordpress/blocks';
-
/**
* Internal dependencies
*/
-import { select } from './controls';
+import { dispatch } from './controls';
/**
* Returns an action object used in signalling that editor has initialized with
* the specified post object and editor settings.
*
- * @param {Object} post Post object.
- * @param {Object} edits Initial edited attributes object.
+ * @param {Object} post Post object.
+ * @param {Object} edits Initial edited attributes object.
+ * @param {Array?} template Block Template.
*
* @return {Object} Action object.
*/
-export function setupEditor( post, edits ) {
+export function setupEditor( post, edits, template ) {
return {
type: 'SETUP_EDITOR',
post,
edits,
+ template,
};
}
@@ -79,338 +76,13 @@ export function updatePost( edits ) {
* Returns an action object used to setup the editor state when first opening an editor.
*
* @param {Object} post Post object.
- * @param {Array} blocks Array of blocks.
*
* @return {Object} Action object.
*/
-export function setupEditorState( post, blocks ) {
+export function setupEditorState( post ) {
return {
type: 'SETUP_EDITOR_STATE',
post,
- blocks,
- };
-}
-
-/**
- * Returns an action object used in signalling that blocks state should be
- * reset to the specified array of blocks, taking precedence over any other
- * content reflected as an edit in state.
- *
- * @param {Array} blocks Array of blocks.
- *
- * @return {Object} Action object.
- */
-export function resetBlocks( blocks ) {
- return {
- type: 'RESET_BLOCKS',
- blocks,
- };
-}
-
-/**
- * Returns an action object used in signalling that blocks have been received.
- * Unlike resetBlocks, these should be appended to the existing known set, not
- * replacing.
- *
- * @param {Object[]} blocks Array of block objects.
- *
- * @return {Object} Action object.
- */
-export function receiveBlocks( blocks ) {
- return {
- type: 'RECEIVE_BLOCKS',
- blocks,
- };
-}
-
-/**
- * Returns an action object used in signalling that the block attributes with
- * the specified client ID has been updated.
- *
- * @param {string} clientId Block client ID.
- * @param {Object} attributes Block attributes to be merged.
- *
- * @return {Object} Action object.
- */
-export function updateBlockAttributes( clientId, attributes ) {
- return {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId,
- attributes,
- };
-}
-
-/**
- * Returns an action object used in signalling that the block with the
- * specified client ID has been updated.
- *
- * @param {string} clientId Block client ID.
- * @param {Object} updates Block attributes to be merged.
- *
- * @return {Object} Action object.
- */
-export function updateBlock( clientId, updates ) {
- return {
- type: 'UPDATE_BLOCK',
- clientId,
- updates,
- };
-}
-
-/**
- * Returns an action object used in signalling that the block with the
- * specified client ID has been selected, optionally accepting a position
- * value reflecting its selection directionality. An initialPosition of -1
- * reflects a reverse selection.
- *
- * @param {string} clientId Block client ID.
- * @param {?number} initialPosition Optional initial position. Pass as -1 to
- * reflect reverse selection.
- *
- * @return {Object} Action object.
- */
-export function selectBlock( clientId, initialPosition = null ) {
- return {
- type: 'SELECT_BLOCK',
- initialPosition,
- clientId,
- };
-}
-
-/**
- * Yields action objects used in signalling that the block preceding the given
- * clientId should be selected.
- *
- * @param {string} clientId Block client ID.
- */
-export function* selectPreviousBlock( clientId ) {
- const previousBlockClientId = yield select(
- 'core/editor',
- 'getPreviousBlockClientId',
- clientId
- );
-
- yield selectBlock( previousBlockClientId, -1 );
-}
-
-/**
- * Yields action objects used in signalling that the block following the given
- * clientId should be selected.
- *
- * @param {string} clientId Block client ID.
- */
-export function* selectNextBlock( clientId ) {
- const nextBlockClientId = yield select(
- 'core/editor',
- 'getNextBlockClientId',
- clientId
- );
-
- yield selectBlock( nextBlockClientId );
-}
-
-export function startMultiSelect() {
- return {
- type: 'START_MULTI_SELECT',
- };
-}
-
-export function stopMultiSelect() {
- return {
- type: 'STOP_MULTI_SELECT',
- };
-}
-
-export function multiSelect( start, end ) {
- return {
- type: 'MULTI_SELECT',
- start,
- end,
- };
-}
-
-export function clearSelectedBlock() {
- return {
- type: 'CLEAR_SELECTED_BLOCK',
- };
-}
-
-/**
- * Returns an action object that enables or disables block selection.
- *
- * @param {boolean} [isSelectionEnabled=true] Whether block selection should
- * be enabled.
-
- * @return {Object} Action object.
- */
-export function toggleSelection( isSelectionEnabled = true ) {
- return {
- type: 'TOGGLE_SELECTION',
- isSelectionEnabled,
- };
-}
-
-/**
- * Returns an action object signalling that a blocks should be replaced with
- * one or more replacement blocks.
- *
- * @param {(string|string[])} clientIds Block client ID(s) to replace.
- * @param {(Object|Object[])} blocks Replacement block(s).
- *
- * @return {Object} Action object.
- */
-export function replaceBlocks( clientIds, blocks ) {
- return {
- type: 'REPLACE_BLOCKS',
- clientIds: castArray( clientIds ),
- blocks: castArray( blocks ),
- time: Date.now(),
- };
-}
-
-/**
- * Returns an action object signalling that a single block should be replaced
- * with one or more replacement blocks.
- *
- * @param {(string|string[])} clientId Block client ID to replace.
- * @param {(Object|Object[])} block Replacement block(s).
- *
- * @return {Object} Action object.
- */
-export function replaceBlock( clientId, block ) {
- return replaceBlocks( clientId, block );
-}
-
-/**
- * Higher-order action creator which, given the action type to dispatch creates
- * an action creator for managing block movement.
- *
- * @param {string} type Action type to dispatch.
- *
- * @return {Function} Action creator.
- */
-function createOnMove( type ) {
- return ( clientIds, rootClientId ) => {
- return {
- clientIds: castArray( clientIds ),
- type,
- rootClientId,
- };
- };
-}
-
-export const moveBlocksDown = createOnMove( 'MOVE_BLOCKS_DOWN' );
-export const moveBlocksUp = createOnMove( 'MOVE_BLOCKS_UP' );
-
-/**
- * Returns an action object signalling that an indexed block should be moved
- * to a new index.
- *
- * @param {?string} clientId The client ID of the block.
- * @param {?string} fromRootClientId Root client ID source.
- * @param {?string} toRootClientId Root client ID destination.
- * @param {number} index The index to move the block into.
- *
- * @return {Object} Action object.
- */
-export function moveBlockToPosition( clientId, fromRootClientId, toRootClientId, index ) {
- return {
- type: 'MOVE_BLOCK_TO_POSITION',
- fromRootClientId,
- toRootClientId,
- clientId,
- index,
- };
-}
-
-/**
- * Returns an action object used in signalling that a single block should be
- * inserted, optionally at a specific index respective a root block list.
- *
- * @param {Object} block Block object to insert.
- * @param {?number} index Index at which block should be inserted.
- * @param {?string} rootClientId Optional root client ID of block list on which to insert.
- * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
- *
- * @return {Object} Action object.
- */
-export function insertBlock( block, index, rootClientId, updateSelection = true ) {
- return insertBlocks( [ block ], index, rootClientId, updateSelection );
-}
-
-/**
- * Returns an action object used in signalling that an array of blocks should
- * be inserted, optionally at a specific index respective a root block list.
- *
- * @param {Object[]} blocks Block objects to insert.
- * @param {?number} index Index at which block should be inserted.
- * @param {?string} rootClientId Optional root client ID of block list on which to insert.
- * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
- *
- * @return {Object} Action object.
- */
-export function insertBlocks( blocks, index, rootClientId, updateSelection = true ) {
- return {
- type: 'INSERT_BLOCKS',
- blocks: castArray( blocks ),
- index,
- rootClientId,
- time: Date.now(),
- updateSelection,
- };
-}
-
-/**
- * Returns an action object used in signalling that the insertion point should
- * be shown.
- *
- * @param {?string} rootClientId Optional root client ID of block list on
- * which to insert.
- * @param {?number} index Index at which block should be inserted.
- *
- * @return {Object} Action object.
- */
-export function showInsertionPoint( rootClientId, index ) {
- return {
- type: 'SHOW_INSERTION_POINT',
- rootClientId,
- index,
- };
-}
-
-/**
- * Returns an action object hiding the insertion point.
- *
- * @return {Object} Action object.
- */
-export function hideInsertionPoint() {
- return {
- type: 'HIDE_INSERTION_POINT',
- };
-}
-
-/**
- * Returns an action object resetting the template validity.
- *
- * @param {boolean} isValid template validity flag.
- *
- * @return {Object} Action object.
- */
-export function setTemplateValidity( isValid ) {
- return {
- type: 'SET_TEMPLATE_VALIDITY',
- isValid,
- };
-}
-
-/**
- * Returns an action object synchronize the template with the list of blocks
- *
- * @return {Object} Action object.
- */
-export function synchronizeTemplate() {
- return {
- type: 'SYNCHRONIZE_TEMPLATE',
};
}
@@ -458,21 +130,6 @@ export function trashPost( postId, postType ) {
};
}
-/**
- * Returns an action object used in signalling that two blocks should be merged
- *
- * @param {string} firstBlockClientId Client ID of the first block to merge.
- * @param {string} secondBlockClientId Client ID of the second block to merge.
- *
- * @return {Object} Action object.
- */
-export function mergeBlocks( firstBlockClientId, secondBlockClientId ) {
- return {
- type: 'MERGE_BLOCKS',
- blocks: [ firstBlockClientId, secondBlockClientId ],
- };
-}
-
/**
* Returns an action object used in signalling that the post should autosave.
*
@@ -513,100 +170,6 @@ export function createUndoLevel() {
return { type: 'CREATE_UNDO_LEVEL' };
}
-/**
- * Yields action objects used in signalling that the blocks corresponding to
- * the set of specified client IDs are to be removed.
- *
- * @param {string|string[]} clientIds Client IDs of blocks to remove.
- * @param {boolean} selectPrevious True if the previous block should be
- * selected when a block is removed.
- */
-export function* removeBlocks( clientIds, selectPrevious = true ) {
- clientIds = castArray( clientIds );
-
- if ( selectPrevious ) {
- yield selectPreviousBlock( clientIds[ 0 ] );
- }
-
- yield {
- type: 'REMOVE_BLOCKS',
- clientIds,
- };
-}
-
-/**
- * Returns an action object used in signalling that the block with the
- * specified client ID is to be removed.
- *
- * @param {string} clientId Client ID of block to remove.
- * @param {boolean} selectPrevious True if the previous block should be
- * selected when a block is removed.
- *
- * @return {Object} Action object.
- */
-export function removeBlock( clientId, selectPrevious ) {
- return removeBlocks( [ clientId ], selectPrevious );
-}
-
-/**
- * Returns an action object used to toggle the block editing mode between
- * visual and HTML modes.
- *
- * @param {string} clientId Block client ID.
- *
- * @return {Object} Action object.
- */
-export function toggleBlockMode( clientId ) {
- return {
- type: 'TOGGLE_BLOCK_MODE',
- clientId,
- };
-}
-
-/**
- * Returns an action object used in signalling that the user has begun to type.
- *
- * @return {Object} Action object.
- */
-export function startTyping() {
- return {
- type: 'START_TYPING',
- };
-}
-
-/**
- * Returns an action object used in signalling that the user has stopped typing.
- *
- * @return {Object} Action object.
- */
-export function stopTyping() {
- return {
- type: 'STOP_TYPING',
- };
-}
-
-/**
- * Returns an action object used in signalling that the caret has entered formatted text.
- *
- * @return {Object} Action object.
- */
-export function enterFormattedText() {
- return {
- type: 'ENTER_FORMATTED_TEXT',
- };
-}
-
-/**
- * Returns an action object used in signalling that the user caret has exited formatted text.
- *
- * @return {Object} Action object.
- */
-export function exitFormattedText() {
- return {
- type: 'EXIT_FORMATTED_TEXT',
- };
-}
-
/**
* Returns an action object used to lock the editor.
*
@@ -727,53 +290,6 @@ export function __experimentalConvertBlockToReusable( clientIds ) {
clientIds: castArray( clientIds ),
};
}
-/**
- * Returns an action object used in signalling that a new block of the default
- * type should be added to the block list.
- *
- * @param {?Object} attributes Optional attributes of the block to assign.
- * @param {?string} rootClientId Optional root client ID of block list on which
- * to append.
- * @param {?number} index Optional index where to insert the default block
- *
- * @return {Object} Action object
- */
-export function insertDefaultBlock( attributes, rootClientId, index ) {
- const block = createBlock( getDefaultBlockName(), attributes );
-
- return insertBlock( block, index, rootClientId );
-}
-
-/**
- * Returns an action object that changes the nested settings of a given block.
- *
- * @param {string} clientId Client ID of the block whose nested setting are
- * being received.
- * @param {Object} settings Object with the new settings for the nested block.
- *
- * @return {Object} Action object
- */
-export function updateBlockListSettings( clientId, settings ) {
- return {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId,
- settings,
- };
-}
-
-/*
- * Returns an action object used in signalling that the editor settings have been updated.
- *
- * @param {Object} settings Updated settings
- *
- * @return {Object} Action object
- */
-export function updateEditorSettings( settings ) {
- return {
- type: 'UPDATE_EDITOR_SETTINGS',
- settings,
- };
-}
/**
* Returns an action object used in signalling that the user has enabled the publish sidebar.
@@ -824,3 +340,59 @@ export function unlockPostSaving( lockName ) {
lockName,
};
}
+
+/**
+ * Returns an action object used to signal that the blocks have been updated.
+ *
+ * @param {Array} blocks Block Array.
+ * @param {?Object} options Optional options.
+ *
+ * @return {Object} Action object
+ */
+export function resetEditorBlocks( blocks, options = {} ) {
+ return {
+ type: 'RESET_EDITOR_BLOCKS',
+ blocks,
+ shouldCreateUndoLevel: options.__unstableShouldCreateUndoLevel !== false,
+ };
+}
+
+/**
+ * Backward compatibility
+ */
+
+const getBlockEditorAction = ( name ) => function* ( ...args ) {
+ yield dispatch( 'core/block-editor', name, ...args );
+};
+
+export const resetBlocks = getBlockEditorAction( 'resetBlocks' );
+export const receiveBlocks = getBlockEditorAction( 'receiveBlocks' );
+export const updateBlock = getBlockEditorAction( 'updateBlock' );
+export const updateBlockAttributes = getBlockEditorAction( 'updateBlockAttributes' );
+export const selectBlock = getBlockEditorAction( 'selectBlock' );
+export const startMultiSelect = getBlockEditorAction( 'startMultiSelect' );
+export const stopMultiSelect = getBlockEditorAction( 'stopMultiSelect' );
+export const multiSelect = getBlockEditorAction( 'multiSelect' );
+export const clearSelectedBlock = getBlockEditorAction( 'clearSelectedBlock' );
+export const toggleSelection = getBlockEditorAction( 'toggleSelection' );
+export const replaceBlocks = getBlockEditorAction( 'replaceBlocks' );
+export const moveBlocksDown = getBlockEditorAction( 'moveBlocksDown' );
+export const moveBlocksUp = getBlockEditorAction( 'moveBlocksUp' );
+export const moveBlockToPosition = getBlockEditorAction( 'moveBlockToPosition' );
+export const insertBlock = getBlockEditorAction( 'insertBlock' );
+export const insertBlocks = getBlockEditorAction( 'insertBlocks' );
+export const showInsertionPoint = getBlockEditorAction( 'showInsertionPoint' );
+export const hideInsertionPoint = getBlockEditorAction( 'hideInsertionPoint' );
+export const setTemplateValidity = getBlockEditorAction( 'setTemplateValidity' );
+export const synchronizeTemplate = getBlockEditorAction( 'synchronizeTemplate' );
+export const mergeBlocks = getBlockEditorAction( 'mergeBlocks' );
+export const removeBlocks = getBlockEditorAction( 'removeBlocks' );
+export const removeBlock = getBlockEditorAction( 'removeBlock' );
+export const toggleBlockMode = getBlockEditorAction( 'toggleBlockMode' );
+export const startTyping = getBlockEditorAction( 'startTyping' );
+export const stopTyping = getBlockEditorAction( 'stopTyping' );
+export const enterFormattedText = getBlockEditorAction( 'enterFormattedText' );
+export const exitFormattedText = getBlockEditorAction( 'exitFormattedText' );
+export const insertDefaultBlock = getBlockEditorAction( 'insertDefaultBlock' );
+export const updateBlockListSettings = getBlockEditorAction( 'updateBlockListSettings' );
+export const updateEditorSettings = getBlockEditorAction( 'updateEditorSettings' );
diff --git a/packages/editor/src/store/controls.js b/packages/editor/src/store/controls.js
index 5012ab244c21c..fc873ad43aa39 100644
--- a/packages/editor/src/store/controls.js
+++ b/packages/editor/src/store/controls.js
@@ -4,26 +4,26 @@
import { createRegistryControl } from '@wordpress/data';
/**
- * Calls a selector using the current state.
+ * Dispatches an action.
*
- * @param {string} storeName Store name.
- * @param {string} selectorName Selector name.
- * @param {Array} args Selector arguments.
+ * @param {string} storeKey Store key.
+ * @param {string} actionName Action name.
+ * @param {Array} args Action arguments.
*
* @return {Object} control descriptor.
*/
-export function select( storeName, selectorName, ...args ) {
+export function dispatch( storeKey, actionName, ...args ) {
return {
- type: 'SELECT',
- storeName,
- selectorName,
+ type: 'DISPATCH',
+ storeKey,
+ actionName,
args,
};
}
const controls = {
- SELECT: createRegistryControl( ( registry ) => ( { storeName, selectorName, args } ) => {
- return registry.select( storeName )[ selectorName ]( ...args );
+ DISPATCH: createRegistryControl( ( registry ) => ( { storeKey, actionName, args } ) => {
+ return registry.dispatch( storeKey )[ actionName ]( ...args );
} ),
};
diff --git a/packages/editor/src/store/defaults.js b/packages/editor/src/store/defaults.js
index dc4111ef641d4..32fd438302c69 100644
--- a/packages/editor/src/store/defaults.js
+++ b/packages/editor/src/store/defaults.js
@@ -1,137 +1,7 @@
-/**
- * WordPress dependencies
- */
-import { __, _x } from '@wordpress/i18n';
-
export const PREFERENCES_DEFAULTS = {
- insertUsage: {},
isPublishSidebarEnabled: true,
};
-/**
- * The default editor settings
- *
- * alignWide boolean Enable/Disable Wide/Full Alignments
- * colors Array Palette colors
- * fontSizes Array Available font sizes
- * imageSizes Array Available image sizes
- * maxWidth number Max width to constraint resizing
- * blockTypes boolean|Array Allowed block types
- * hasFixedToolbar boolean Whether or not the editor toolbar is fixed
- * focusMode boolean Whether the focus mode is enabled or not
- * richEditingEnabled boolean Whether rich editing is enabled or not
- */
-export const EDITOR_SETTINGS_DEFAULTS = {
- alignWide: false,
- colors: [
- {
- name: __( 'Pale pink' ),
- slug: 'pale-pink',
- color: '#f78da7',
- },
- { name: __( 'Vivid red' ),
- slug: 'vivid-red',
- color: '#cf2e2e',
- },
- {
- name: __( 'Luminous vivid orange' ),
- slug: 'luminous-vivid-orange',
- color: '#ff6900',
- },
- {
- name: __( 'Luminous vivid amber' ),
- slug: 'luminous-vivid-amber',
- color: '#fcb900',
- },
- {
- name: __( 'Light green cyan' ),
- slug: 'light-green-cyan',
- color: '#7bdcb5',
- },
- {
- name: __( 'Vivid green cyan' ),
- slug: 'vivid-green-cyan',
- color: '#00d084',
- },
- {
- name: __( 'Pale cyan blue' ),
- slug: 'pale-cyan-blue',
- color: '#8ed1fc',
- },
- {
- name: __( 'Vivid cyan blue' ),
- slug: 'vivid-cyan-blue',
- color: '#0693e3',
- },
- {
- name: __( 'Very light gray' ),
- slug: 'very-light-gray',
- color: '#eeeeee',
- },
- {
- name: __( 'Cyan bluish gray' ),
- slug: 'cyan-bluish-gray',
- color: '#abb8c3',
- },
- {
- name: __( 'Very dark gray' ),
- slug: 'very-dark-gray',
- color: '#313131',
- },
- ],
-
- fontSizes: [
- {
- name: _x( 'Small', 'font size name' ),
- size: 13,
- slug: 'small',
- },
- {
- name: _x( 'Normal', 'font size name' ),
- size: 16,
- slug: 'normal',
- },
- {
- name: _x( 'Medium', 'font size name' ),
- size: 20,
- slug: 'medium',
- },
- {
- name: _x( 'Large', 'font size name' ),
- size: 36,
- slug: 'large',
- },
- {
- name: _x( 'Huge', 'font size name' ),
- size: 48,
- slug: 'huge',
- },
- ],
-
- imageSizes: [
- { slug: 'thumbnail', label: __( 'Thumbnail' ) },
- { slug: 'medium', label: __( 'Medium' ) },
- { slug: 'large', label: __( 'Large' ) },
- { slug: 'full', label: __( 'Full Size' ) },
- ],
-
- // This is current max width of the block inner area
- // It's used to constraint image resizing and this value could be overridden later by themes
- maxWidth: 580,
-
- // Allowed block types for the editor, defaulting to true (all supported).
- allowedBlockTypes: true,
-
- // Maximum upload size in bytes allowed for the site.
- maxUploadFileSize: 0,
-
- // List of allowed mime types and file extensions.
- allowedMimeTypes: null,
-
- // Whether richs editing is enabled or not.
- richEditingEnabled: true,
-};
-
/**
* Default initial edits state.
*
diff --git a/packages/editor/src/store/effects.js b/packages/editor/src/store/effects.js
index de9552ff5c9b6..de77fbc45aab5 100644
--- a/packages/editor/src/store/effects.js
+++ b/packages/editor/src/store/effects.js
@@ -1,41 +1,23 @@
/**
* External dependencies
*/
-import { compact, has } from 'lodash';
+import { has } from 'lodash';
/**
* WordPress dependencies
*/
-import { speak } from '@wordpress/a11y';
import {
parse,
- getBlockType,
- switchToBlockType,
- doBlocksMatchTemplate,
synchronizeBlocksWithTemplate,
} from '@wordpress/blocks';
-import { _n, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import {
setupEditorState,
- replaceBlocks,
- selectBlock,
- resetBlocks,
- setTemplateValidity,
- insertDefaultBlock,
+ resetEditorBlocks,
} from './actions';
-import {
- getBlock,
- getBlocks,
- getBlockCount,
- getSelectedBlockCount,
- getTemplate,
- getTemplateLock,
- isValidTemplate,
-} from './selectors';
import {
fetchReusableBlocks,
saveReusableBlocks,
@@ -53,53 +35,6 @@ import {
refreshPost,
} from './effects/posts';
-/**
- * Block validity is a function of blocks state (at the point of a
- * reset) and the template setting. As a compromise to its placement
- * across distinct parts of state, it is implemented here as a side-
- * effect of the block reset action.
- *
- * @param {Object} action RESET_BLOCKS action.
- * @param {Object} store Store instance.
- *
- * @return {?Object} New validity set action if validity has changed.
- */
-export function validateBlocksToTemplate( action, store ) {
- const state = store.getState();
- const template = getTemplate( state );
- const templateLock = getTemplateLock( state );
-
- // Unlocked templates are considered always valid because they act
- // as default values only.
- const isBlocksValidToTemplate = (
- ! template ||
- templateLock !== 'all' ||
- doBlocksMatchTemplate( action.blocks, template )
- );
-
- // Update if validity has changed.
- if ( isBlocksValidToTemplate !== isValidTemplate( state ) ) {
- return setTemplateValidity( isBlocksValidToTemplate );
- }
-}
-
-/**
- * Effect handler which will return a default block insertion action if there
- * are no other blocks at the root of the editor. This is expected to be used
- * in actions which may result in no blocks remaining in the editor (removal,
- * replacement, etc).
- *
- * @param {Object} action Action which had initiated the effect handler.
- * @param {Object} store Store instance.
- *
- * @return {?Object} Default block insert action, if no other blocks exist.
- */
-export function ensureDefaultBlock( action, store ) {
- if ( ! getBlockCount( store.getState() ) ) {
- return insertDefaultBlock();
- }
-}
-
export default {
REQUEST_POST_UPDATE: ( action, store ) => {
requestPostUpdate( action, store );
@@ -113,55 +48,8 @@ export default {
REFRESH_POST: ( action, store ) => {
refreshPost( action, store );
},
- MERGE_BLOCKS( action, store ) {
- const { dispatch } = store;
- const state = store.getState();
- const [ firstBlockClientId, secondBlockClientId ] = action.blocks;
- const blockA = getBlock( state, firstBlockClientId );
- const blockType = getBlockType( blockA.name );
-
- // Only focus the previous block if it's not mergeable
- if ( ! blockType.merge ) {
- dispatch( selectBlock( blockA.clientId ) );
- return;
- }
-
- // We can only merge blocks with similar types
- // thus, we transform the block to merge first
- const blockB = getBlock( state, secondBlockClientId );
- const blocksWithTheSameType = blockA.name === blockB.name ?
- [ blockB ] :
- switchToBlockType( blockB, blockA.name );
-
- // If the block types can not match, do nothing
- if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) {
- return;
- }
-
- // Calling the merge to update the attributes and remove the block to be merged
- const updatedAttributes = blockType.merge(
- blockA.attributes,
- blocksWithTheSameType[ 0 ].attributes
- );
-
- dispatch( selectBlock( blockA.clientId, -1 ) );
- dispatch( replaceBlocks(
- [ blockA.clientId, blockB.clientId ],
- [
- {
- ...blockA,
- attributes: {
- ...blockA.attributes,
- ...updatedAttributes,
- },
- },
- ...blocksWithTheSameType.slice( 1 ),
- ]
- ) );
- },
- SETUP_EDITOR( action, store ) {
- const { post, edits } = action;
- const state = store.getState();
+ SETUP_EDITOR( action ) {
+ const { post, edits, template } = action;
// In order to ensure maximum of a single parse during setup, edits are
// included as part of editor setup action. Assume edited content as
@@ -177,33 +65,14 @@ export default {
// Apply a template for new posts only, if exists.
const isNewPost = post.status === 'auto-draft';
- const template = getTemplate( state );
if ( isNewPost && template ) {
blocks = synchronizeBlocksWithTemplate( blocks, template );
}
- const setupAction = setupEditorState( post, blocks );
-
- return compact( [
- setupAction,
-
- // TODO: This is temporary, necessary only so long as editor setup
- // is a separate action from block resetting.
- //
- // See: https://github.com/WordPress/gutenberg/pull/9403
- validateBlocksToTemplate( setupAction, store ),
- ] );
- },
- RESET_BLOCKS: [
- validateBlocksToTemplate,
- ],
- SYNCHRONIZE_TEMPLATE( action, { getState } ) {
- const state = getState();
- const blocks = getBlocks( state );
- const template = getTemplate( state );
- const updatedBlockList = synchronizeBlocksWithTemplate( blocks, template );
-
- return resetBlocks( updatedBlockList );
+ return [
+ resetEditorBlocks( blocks ),
+ setupEditorState( post ),
+ ];
},
FETCH_REUSABLE_BLOCKS: ( action, store ) => {
fetchReusableBlocks( action, store );
@@ -217,16 +86,4 @@ export default {
RECEIVE_REUSABLE_BLOCKS: receiveReusableBlocks,
CONVERT_BLOCK_TO_STATIC: convertBlockToStatic,
CONVERT_BLOCK_TO_REUSABLE: convertBlockToReusable,
- REMOVE_BLOCKS: [
- ensureDefaultBlock,
- ],
- REPLACE_BLOCKS: [
- ensureDefaultBlock,
- ],
- MULTI_SELECT: ( action, { getState } ) => {
- const blockCount = getSelectedBlockCount( getState() );
-
- /* translators: %s: number of selected blocks */
- speak( sprintf( _n( '%s block selected.', '%s blocks selected.', blockCount ), blockCount ), 'assertive' );
- },
};
diff --git a/packages/editor/src/store/effects/reusable-blocks.js b/packages/editor/src/store/effects/reusable-blocks.js
index acfa3d0aaaefb..63bc00169c543 100644
--- a/packages/editor/src/store/effects/reusable-blocks.js
+++ b/packages/editor/src/store/effects/reusable-blocks.js
@@ -19,23 +19,17 @@ import { __ } from '@wordpress/i18n';
// TODO: Ideally this would be the only dispatch in scope. This requires either
// refactoring editor actions to yielded controls, or replacing direct dispatch
// on the editor store with action creators (e.g. `REMOVE_REUSABLE_BLOCK`).
-import { dispatch as dataDispatch } from '@wordpress/data';
+import { dispatch as dataDispatch, select } from '@wordpress/data';
/**
* Internal dependencies
*/
import {
__experimentalReceiveReusableBlocks as receiveReusableBlocksAction,
- removeBlocks,
- replaceBlocks,
- receiveBlocks,
__experimentalSaveReusableBlock as saveReusableBlock,
} from '../actions';
import {
__experimentalGetReusableBlock as getReusableBlock,
- getBlock,
- getBlocks,
- getBlocksByClientId,
} from '../selectors';
import { getPostRawValue } from '../reducer';
@@ -122,7 +116,7 @@ export const saveReusableBlocks = async ( action, store ) => {
const { dispatch } = store;
const state = store.getState();
const { clientId, title, isTemporary } = getReusableBlock( state, id );
- const reusableBlock = getBlock( state, clientId );
+ const reusableBlock = select( 'core/block-editor' ).getBlock( clientId );
const content = serialize( reusableBlock.name === 'core/template' ? reusableBlock.innerBlocks : reusableBlock );
const data = isTemporary ? { title, content, status: 'publish' } : { id, title, content, status: 'publish' };
@@ -140,6 +134,8 @@ export const saveReusableBlocks = async ( action, store ) => {
dataDispatch( 'core/notices' ).createSuccessNotice( message, {
id: REUSABLE_BLOCK_NOTICE_ID,
} );
+
+ dataDispatch( 'core/block-editor' ).__unstableSaveReusableBlock( id, updatedReusableBlock.id );
} catch ( error ) {
dispatch( { type: 'SAVE_REUSABLE_BLOCK_FAILURE', id } );
dataDispatch( 'core/notices' ).createErrorNotice( error.message, {
@@ -172,7 +168,7 @@ export const deleteReusableBlocks = async ( action, store ) => {
}
// Remove any other blocks that reference this reusable block
- const allBlocks = getBlocks( getState() );
+ const allBlocks = select( 'core/block-editor' ).getBlocks();
const associatedBlocks = allBlocks.filter( ( block ) => isReusableBlock( block ) && block.attributes.ref === id );
const associatedBlockClientIds = associatedBlocks.map( ( block ) => block.clientId );
@@ -185,10 +181,10 @@ export const deleteReusableBlocks = async ( action, store ) => {
} );
// Remove the parsed block.
- dispatch( removeBlocks( [
+ dataDispatch( 'core/block-editor' ).removeBlocks( [
...associatedBlockClientIds,
reusableBlock.clientId,
- ] ) );
+ ] );
try {
await apiFetch( {
@@ -220,10 +216,9 @@ export const deleteReusableBlocks = async ( action, store ) => {
* Receive Reusable Blocks Effect Handler.
*
* @param {Object} action action object.
- * @return {Object} receive blocks action
*/
export const receiveReusableBlocks = ( action ) => {
- return receiveBlocks( map( action.results, 'parsedBlock' ) );
+ dataDispatch( 'core/block-editor' ).receiveBlocks( map( action.results, 'parsedBlock' ) );
};
/**
@@ -234,16 +229,16 @@ export const receiveReusableBlocks = ( action ) => {
*/
export const convertBlockToStatic = ( action, store ) => {
const state = store.getState();
- const oldBlock = getBlock( state, action.clientId );
+ const oldBlock = select( 'core/block-editor' ).getBlock( action.clientId );
const reusableBlock = getReusableBlock( state, oldBlock.attributes.ref );
- const referencedBlock = getBlock( state, reusableBlock.clientId );
+ const referencedBlock = select( 'core/block-editor' ).getBlock( reusableBlock.clientId );
let newBlocks;
if ( referencedBlock.name === 'core/template' ) {
newBlocks = referencedBlock.innerBlocks.map( ( innerBlock ) => cloneBlock( innerBlock ) );
} else {
newBlocks = [ cloneBlock( referencedBlock ) ];
}
- store.dispatch( replaceBlocks( oldBlock.clientId, newBlocks ) );
+ dataDispatch( 'core/block-editor' ).replaceBlocks( oldBlock.clientId, newBlocks );
};
/**
@@ -253,20 +248,20 @@ export const convertBlockToStatic = ( action, store ) => {
* @param {Object} store Redux Store.
*/
export const convertBlockToReusable = ( action, store ) => {
- const { getState, dispatch } = store;
+ const { dispatch } = store;
let parsedBlock;
if ( action.clientIds.length === 1 ) {
- parsedBlock = getBlock( getState(), action.clientIds[ 0 ] );
+ parsedBlock = select( 'core/block-editor' ).getBlock( action.clientIds[ 0 ] );
} else {
parsedBlock = createBlock(
'core/template',
{},
- getBlocksByClientId( getState(), action.clientIds )
+ select( 'core/block-editor' ).getBlocksByClientId( action.clientIds )
);
// This shouldn't be necessary but at the moment
// we expect the content of the shared blocks to live in the blocks state.
- dispatch( receiveBlocks( [ parsedBlock ] ) );
+ dataDispatch( 'core/block-editor' ).receiveBlocks( [ parsedBlock ] );
}
const reusableBlock = {
@@ -282,13 +277,13 @@ export const convertBlockToReusable = ( action, store ) => {
dispatch( saveReusableBlock( reusableBlock.id ) );
- dispatch( replaceBlocks(
+ dataDispatch( 'core/block-editor' ).replaceBlocks(
action.clientIds,
createBlock( 'core/block', {
ref: reusableBlock.id,
} )
- ) );
+ );
// Re-add the original block to the store, since replaceBlock() will have removed it
- dispatch( receiveBlocks( [ parsedBlock ] ) );
+ dataDispatch( 'core/block-editor' ).receiveBlocks( [ parsedBlock ] );
};
diff --git a/packages/editor/src/store/effects/test/reusable-blocks.js b/packages/editor/src/store/effects/test/reusable-blocks.js
index 071e4f07f4f6f..1d783b1dd9fe1 100644
--- a/packages/editor/src/store/effects/test/reusable-blocks.js
+++ b/packages/editor/src/store/effects/test/reusable-blocks.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { noop, reduce } from 'lodash';
+import { noop } from 'lodash';
/**
* WordPress dependencies
@@ -12,6 +12,7 @@ import {
unregisterBlockType,
createBlock,
} from '@wordpress/blocks';
+import { dispatch as dataDispatch, select as dataSelect } from '@wordpress/data';
/**
* Internal dependencies
@@ -25,11 +26,8 @@ import {
convertBlockToReusable,
} from '../reusable-blocks';
import {
- resetBlocks,
- receiveBlocks,
__experimentalSaveReusableBlock as saveReusableBlock,
__experimentalDeleteReusableBlock as deleteReusableBlock,
- removeBlocks,
__experimentalConvertBlockToReusable as convertBlockToReusableAction,
__experimentalConvertBlockToStatic as convertBlockToStaticAction,
__experimentalReceiveReusableBlocks as receiveReusableBlocksAction,
@@ -50,6 +48,7 @@ describe( 'reusable blocks effects', () => {
name: { type: 'string' },
},
} );
+
registerBlockType( 'core/block', {
title: 'Reusable Block',
category: 'common',
@@ -252,10 +251,8 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( () => parsedBlock );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -267,6 +264,8 @@ describe( 'reusable blocks effects', () => {
id: 123,
updatedId: 456,
} );
+
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
it( 'should handle an API error', async () => {
@@ -286,10 +285,8 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( () => parsedBlock );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -299,6 +296,8 @@ describe( 'reusable blocks effects', () => {
type: 'SAVE_REUSABLE_BLOCK_FAILURE',
id: 123,
} );
+
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
} );
@@ -310,9 +309,13 @@ describe( 'reusable blocks effects', () => {
},
] );
- expect( receiveReusableBlocks( action ) ).toEqual( receiveBlocks( [
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'receiveBlocks' ).mockImplementation( () => {} );
+ receiveReusableBlocks( action );
+ expect( dataDispatch( 'core/block-editor' ).receiveBlocks ).toHaveBeenCalledWith( [
{ clientId: 'broccoli' },
- ] ) );
+ ] );
+
+ dataDispatch( 'core/block-editor' ).receiveBlocks.mockReset();
} );
} );
@@ -335,11 +338,12 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- resetBlocks( [ associatedBlock ] ),
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlocks' ).mockImplementation( () => [
+ associatedBlock,
+ parsedBlock,
+ ] );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'removeBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -352,8 +356,8 @@ describe( 'reusable blocks effects', () => {
optimist: expect.any( Object ),
} );
- expect( dispatch ).toHaveBeenCalledWith(
- removeBlocks( [ associatedBlock.clientId, parsedBlock.clientId ] )
+ expect( dataDispatch( 'core/block-editor' ).removeBlocks ).toHaveBeenCalledWith(
+ [ associatedBlock.clientId, parsedBlock.clientId ]
);
expect( dispatch ).toHaveBeenCalledWith( {
@@ -361,6 +365,9 @@ describe( 'reusable blocks effects', () => {
id: 123,
optimist: expect.any( Object ),
} );
+
+ dataDispatch( 'core/block-editor' ).removeBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlocks.mockReset();
} );
it( 'should handle an API error', async () => {
@@ -379,11 +386,11 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
-
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlocks' ).mockImplementation( () => [
+ parsedBlock,
+ ] );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'removeBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -395,16 +402,19 @@ describe( 'reusable blocks effects', () => {
id: 123,
optimist: expect.any( Object ),
} );
+ dataDispatch( 'core/block-editor' ).removeBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlocks.mockReset();
} );
it( 'should not save reusable blocks with temporary IDs', async () => {
const reusableBlock = { id: 'reusable1', title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlocks' ).mockImplementation( () => [
+ parsedBlock,
+ ] );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'removeBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
@@ -412,6 +422,8 @@ describe( 'reusable blocks effects', () => {
await deleteReusableBlocks( deleteReusableBlock( 'reusable1' ), store );
expect( dispatch ).not.toHaveBeenCalled();
+ dataDispatch( 'core/block-editor' ).removeBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlocks.mockReset();
} );
} );
@@ -421,28 +433,29 @@ describe( 'reusable blocks effects', () => {
const reusableBlock = { id: 123, title: 'My cool block' };
const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } );
- const state = reduce( [
- resetBlocks( [ associatedBlock ] ),
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( ( id ) =>
+ associatedBlock.clientId === id ? associatedBlock : parsedBlock
+ );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'replaceBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
convertBlockToStatic( convertBlockToStaticAction( associatedBlock.clientId ), store );
- expect( dispatch ).toHaveBeenCalledWith( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ associatedBlock.clientId ],
- blocks: [
+ expect( dataDispatch( 'core/block-editor' ).replaceBlocks ).toHaveBeenCalledWith(
+ associatedBlock.clientId,
+ [
expect.objectContaining( {
name: 'core/test-block',
attributes: { name: 'Big Bird' },
} ),
- ],
- time: expect.any( Number ),
- } );
+ ]
+ );
+
+ dataDispatch( 'core/block-editor' ).replaceBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
it( 'should convert a reusable block with nested blocks into a static block', () => {
@@ -452,22 +465,20 @@ describe( 'reusable blocks effects', () => {
createBlock( 'core/test-block', { name: 'Oscar the Grouch' } ),
createBlock( 'core/test-block', { name: 'Cookie Monster' } ),
] );
-
- const state = reduce( [
- resetBlocks( [ associatedBlock ] ),
- receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ),
- receiveBlocks( [ parsedBlock ] ),
- ], reducer, undefined );
+ const state = reducer( undefined, receiveReusableBlocksAction( [ { reusableBlock, parsedBlock } ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( ( id ) =>
+ associatedBlock.clientId === id ? associatedBlock : parsedBlock
+ );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'replaceBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
const store = { getState: () => state, dispatch };
convertBlockToStatic( convertBlockToStaticAction( associatedBlock.clientId ), store );
- expect( dispatch ).toHaveBeenCalledWith( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ associatedBlock.clientId ],
- blocks: [
+ expect( dataDispatch( 'core/block-editor' ).replaceBlocks ).toHaveBeenCalledWith(
+ associatedBlock.clientId,
+ [
expect.objectContaining( {
name: 'core/test-block',
attributes: { name: 'Big Bird' },
@@ -480,19 +491,25 @@ describe( 'reusable blocks effects', () => {
} ),
],
} ),
- ],
- time: expect.any( Number ),
- } );
+ ]
+ );
+
+ dataDispatch( 'core/block-editor' ).replaceBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
} );
describe( 'convertBlockToReusable', () => {
it( 'should convert a static block into a reusable block', () => {
const staticBlock = createBlock( 'core/block', { ref: 123 } );
- const state = reducer( undefined, resetBlocks( [ staticBlock ] ) );
+ jest.spyOn( dataSelect( 'core/block-editor' ), 'getBlock' ).mockImplementation( ( ) =>
+ staticBlock
+ );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'replaceBlocks' ).mockImplementation( () => {} );
+ jest.spyOn( dataDispatch( 'core/block-editor' ), 'receiveBlocks' ).mockImplementation( () => {} );
const dispatch = jest.fn();
- const store = { getState: () => state, dispatch };
+ const store = { getState: () => {}, dispatch };
convertBlockToReusable( convertBlockToReusableAction( staticBlock.clientId ), store );
@@ -511,21 +528,21 @@ describe( 'reusable blocks effects', () => {
saveReusableBlock( expect.stringMatching( /^reusable/ ) ),
);
- expect( dispatch ).toHaveBeenCalledWith( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ staticBlock.clientId ],
- blocks: [
- expect.objectContaining( {
- name: 'core/block',
- attributes: { ref: expect.stringMatching( /^reusable/ ) },
- } ),
- ],
- time: expect.any( Number ),
- } );
+ expect( dataDispatch( 'core/block-editor' ).replaceBlocks ).toHaveBeenCalledWith(
+ [ staticBlock.clientId ],
+ expect.objectContaining( {
+ name: 'core/block',
+ attributes: { ref: expect.stringMatching( /^reusable/ ) },
+ } ),
+ );
- expect( dispatch ).toHaveBeenCalledWith(
- receiveBlocks( [ staticBlock ] ),
+ expect( dataDispatch( 'core/block-editor' ).receiveBlocks ).toHaveBeenCalledWith(
+ [ staticBlock ]
);
+
+ dataDispatch( 'core/block-editor' ).replaceBlocks.mockReset();
+ dataDispatch( 'core/block-editor' ).receiveBlocks.mockReset();
+ dataSelect( 'core/block-editor' ).getBlock.mockReset();
} );
} );
} );
diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js
index 6e305185c96e7..2cc1791a0a4a0 100644
--- a/packages/editor/src/store/reducer.js
+++ b/packages/editor/src/store/reducer.js
@@ -5,37 +5,29 @@ import optimist from 'redux-optimist';
import {
flow,
reduce,
- first,
- last,
omit,
- without,
mapValues,
- omitBy,
keys,
isEqual,
- isEmpty,
- overSome,
+ last,
} from 'lodash';
/**
* WordPress dependencies
*/
-import { isReusableBlock } from '@wordpress/blocks';
import { combineReducers } from '@wordpress/data';
import { addQueryArgs } from '@wordpress/url';
/**
* Internal dependencies
*/
-import withHistory from '../utils/with-history';
-import withChangeDetection from '../utils/with-change-detection';
import {
PREFERENCES_DEFAULTS,
- EDITOR_SETTINGS_DEFAULTS,
INITIAL_EDITS_DEFAULTS,
} from './defaults';
-import { insertAt, moveTo } from './array';
import { EDIT_MERGE_PROPERTIES } from './constants';
+import withChangeDetection from '../utils/with-change-detection';
+import withHistory from '../utils/with-history';
/**
* Returns a post attribute value, flattening nested rendered content using its
@@ -53,101 +45,6 @@ export function getPostRawValue( value ) {
return value;
}
-/**
- * Given an array of blocks, returns an object where each key is a nesting
- * context, the value of which is an array of block client IDs existing within
- * that nesting context.
- *
- * @param {Array} blocks Blocks to map.
- * @param {?string} rootClientId Assumed root client ID.
- *
- * @return {Object} Block order map object.
- */
-function mapBlockOrder( blocks, rootClientId = '' ) {
- const result = { [ rootClientId ]: [] };
-
- blocks.forEach( ( block ) => {
- const { clientId, innerBlocks } = block;
-
- result[ rootClientId ].push( clientId );
-
- Object.assign( result, mapBlockOrder( innerBlocks, clientId ) );
- } );
-
- return result;
-}
-
-/**
- * Helper method to iterate through all blocks, recursing into inner blocks,
- * applying a transformation function to each one.
- * Returns a flattened object with the transformed blocks.
- *
- * @param {Array} blocks Blocks to flatten.
- * @param {Function} transform Transforming function to be applied to each block.
- *
- * @return {Object} Flattened object.
- */
-function flattenBlocks( blocks, transform ) {
- const result = {};
-
- const stack = [ ...blocks ];
- while ( stack.length ) {
- const { innerBlocks, ...block } = stack.shift();
- stack.push( ...innerBlocks );
- result[ block.clientId ] = transform( block );
- }
-
- return result;
-}
-
-/**
- * Given an array of blocks, returns an object containing all blocks, without
- * attributes, recursing into inner blocks. Keys correspond to the block client
- * ID, the value of which is the attributes object.
- *
- * @param {Array} blocks Blocks to flatten.
- *
- * @return {Object} Flattened block attributes object.
- */
-function getFlattenedBlocksWithoutAttributes( blocks ) {
- return flattenBlocks( blocks, ( block ) => omit( block, 'attributes' ) );
-}
-
-/**
- * Given an array of blocks, returns an object containing all block attributes,
- * recursing into inner blocks. Keys correspond to the block client ID, the
- * value of which is the attributes object.
- *
- * @param {Array} blocks Blocks to flatten.
- *
- * @return {Object} Flattened block attributes object.
- */
-function getFlattenedBlockAttributes( blocks ) {
- return flattenBlocks( blocks, ( block ) => block.attributes );
-}
-
-/**
- * Given a block order map object, returns *all* of the block client IDs that are
- * a descendant of the given root client ID.
- *
- * Calling this with `rootClientId` set to `''` results in a list of client IDs
- * that are in the post. That is, it excludes blocks like fetched reusable
- * blocks which are stored into state but not visible.
- *
- * @param {Object} blocksOrder Object that maps block client IDs to a list of
- * nested block client IDs.
- * @param {?string} rootClientId The root client ID to search. Defaults to ''.
- *
- * @return {Array} List of descendant client IDs.
- */
-function getNestedBlockClientIds( blocksOrder, rootClientId = '' ) {
- return reduce( blocksOrder[ rootClientId ], ( result, clientId ) => [
- ...result,
- clientId,
- ...getNestedBlockClientIds( blocksOrder, clientId ),
- ], [] );
-}
-
/**
* Returns an object against which it is safe to perform mutating operations,
* given the original object and its current working copy.
@@ -178,24 +75,6 @@ export function hasSameKeys( a, b ) {
return isEqual( keys( a ), keys( b ) );
}
-/**
- * Returns true if, given the currently dispatching action and the previously
- * dispatched action, the two actions are updating the same block attribute, or
- * false otherwise.
- *
- * @param {Object} action Currently dispatching action.
- * @param {Object} previousAction Previously dispatched action.
- *
- * @return {boolean} Whether actions are updating the same block attribute.
- */
-export function isUpdatingSameBlockAttribute( action, previousAction ) {
- return (
- action.type === 'UPDATE_BLOCK_ATTRIBUTES' &&
- action.clientId === previousAction.clientId &&
- hasSameKeys( action.attributes, previousAction.attributes )
- );
-}
-
/**
* Returns true if, given the currently dispatching action and the previously
* dispatched action, the two actions are editing the same post property, or
@@ -224,110 +103,17 @@ export function isUpdatingSamePostProperty( action, previousAction ) {
* @return {boolean} Whether to overwrite present state.
*/
export function shouldOverwriteState( action, previousAction ) {
+ if ( action.type === 'RESET_EDITOR_BLOCKS' ) {
+ return ! action.shouldCreateUndoLevel;
+ }
+
if ( ! previousAction || action.type !== previousAction.type ) {
return false;
}
- return overSome( [
- isUpdatingSameBlockAttribute,
- isUpdatingSamePostProperty,
- ] )( action, previousAction );
+ return isUpdatingSamePostProperty( action, previousAction );
}
-/**
- * Higher-order reducer targeting the combined editor reducer, augmenting
- * block client IDs in remove action to include cascade of inner blocks.
- *
- * @param {Function} reducer Original reducer function.
- *
- * @return {Function} Enhanced reducer function.
- */
-const withInnerBlocksRemoveCascade = ( reducer ) => ( state, action ) => {
- if ( state && action.type === 'REMOVE_BLOCKS' ) {
- const clientIds = [ ...action.clientIds ];
-
- // For each removed client ID, include its inner blocks to remove,
- // recursing into those so long as inner blocks exist.
- for ( let i = 0; i < clientIds.length; i++ ) {
- clientIds.push( ...state.blocks.order[ clientIds[ i ] ] );
- }
-
- action = { ...action, clientIds };
- }
-
- return reducer( state, action );
-};
-
-/**
- * Higher-order reducer which targets the combined blocks reducer and handles
- * the `RESET_BLOCKS` action. When dispatched, this action will replace all
- * blocks that exist in the post, leaving blocks that exist only in state (e.g.
- * reusable blocks) alone.
- *
- * @param {Function} reducer Original reducer function.
- *
- * @return {Function} Enhanced reducer function.
- */
-const withBlockReset = ( reducer ) => ( state, action ) => {
- if ( state && action.type === 'RESET_BLOCKS' ) {
- const visibleClientIds = getNestedBlockClientIds( state.order );
- return {
- ...state,
- byClientId: {
- ...omit( state.byClientId, visibleClientIds ),
- ...getFlattenedBlocksWithoutAttributes( action.blocks ),
- },
- attributes: {
- ...omit( state.attributes, visibleClientIds ),
- ...getFlattenedBlockAttributes( action.blocks ),
- },
- order: {
- ...omit( state.order, visibleClientIds ),
- ...mapBlockOrder( action.blocks ),
- },
- };
- }
-
- return reducer( state, action );
-};
-
-/**
- * Higher-order reducer which targets the combined blocks reducer and handles
- * the `SAVE_REUSABLE_BLOCK_SUCCESS` action. This action can't be handled by
- * regular reducers and needs a higher-order reducer since it needs access to
- * both `byClientId` and `attributes` simultaneously.
- *
- * @param {Function} reducer Original reducer function.
- *
- * @return {Function} Enhanced reducer function.
- */
-const withSaveReusableBlock = ( reducer ) => ( state, action ) => {
- if ( state && action.type === 'SAVE_REUSABLE_BLOCK_SUCCESS' ) {
- const { id, updatedId } = action;
-
- // If a temporary reusable block is saved, we swap the temporary id with the final one
- if ( id === updatedId ) {
- return state;
- }
-
- state = { ...state };
-
- state.attributes = mapValues( state.attributes, ( attributes, clientId ) => {
- const { name } = state.byClientId[ clientId ];
- if ( name === 'core/block' && attributes.ref === id ) {
- return {
- ...attributes,
- ref: updatedId,
- };
- }
-
- return attributes;
- } );
- }
-
- return reducer( state, action );
-};
-
/**
* Undoable reducer returning the editor post state, including blocks parsed
* from current HTML markup.
@@ -345,15 +131,31 @@ const withSaveReusableBlock = ( reducer ) => ( state, action ) => {
export const editor = flow( [
combineReducers,
- withInnerBlocksRemoveCascade,
-
- // Track undo history, starting at editor initialization.
withHistory( {
resetTypes: [ 'SETUP_EDITOR_STATE' ],
- ignoreTypes: [ 'RECEIVE_BLOCKS', 'RESET_POST', 'UPDATE_POST' ],
+ ignoreTypes: [
+ 'RECEIVE_BLOCKS',
+ 'RESET_POST',
+ 'UPDATE_POST',
+ ],
shouldOverwriteState,
} ),
] )( {
+ // Track whether changes exist, resetting at each post save. Relies on
+ // editor initialization firing post reset as an effect.
+ blocks: withChangeDetection( {
+ resetTypes: [ 'SETUP_EDITOR_STATE', 'REQUEST_POST_UPDATE_START' ],
+ } )( ( state = { value: [] }, action ) => {
+ switch ( action.type ) {
+ case 'RESET_EDITOR_BLOCKS':
+ if ( action.blocks === state.value ) {
+ return state;
+ }
+ return { value: action.blocks };
+ }
+
+ return state;
+ } ),
edits( state = {}, action ) {
switch ( action.type ) {
case 'EDIT_POST':
@@ -373,14 +175,6 @@ export const editor = flow( [
return result;
}, state );
-
- case 'RESET_BLOCKS':
- if ( 'content' in state ) {
- return omit( state, 'content' );
- }
-
- return state;
-
case 'UPDATE_POST':
case 'RESET_POST':
const getCanonicalValue = action.type === 'UPDATE_POST' ?
@@ -396,284 +190,16 @@ export const editor = flow( [
delete result[ key ];
return result;
}, state );
+ case 'RESET_EDITOR_BLOCKS':
+ if ( 'content' in state ) {
+ return omit( state, 'content' );
+ }
+
+ return state;
}
return state;
},
-
- blocks: flow( [
- combineReducers,
-
- withBlockReset,
-
- withSaveReusableBlock,
-
- // Track whether changes exist, resetting at each post save. Relies on
- // editor initialization firing post reset as an effect.
- withChangeDetection( {
- resetTypes: [ 'SETUP_EDITOR_STATE', 'REQUEST_POST_UPDATE_START' ],
- ignoreTypes: [ 'RECEIVE_BLOCKS', 'RESET_POST', 'UPDATE_POST' ],
- } ),
- ] )( {
- byClientId( state = {}, action ) {
- switch ( action.type ) {
- case 'SETUP_EDITOR_STATE':
- return getFlattenedBlocksWithoutAttributes( action.blocks );
-
- case 'RECEIVE_BLOCKS':
- return {
- ...state,
- ...getFlattenedBlocksWithoutAttributes( action.blocks ),
- };
-
- case 'UPDATE_BLOCK':
- // Ignore updates if block isn't known
- if ( ! state[ action.clientId ] ) {
- return state;
- }
-
- // Do nothing if only attributes change.
- const changes = omit( action.updates, 'attributes' );
- if ( isEmpty( changes ) ) {
- return state;
- }
-
- return {
- ...state,
- [ action.clientId ]: {
- ...state[ action.clientId ],
- ...changes,
- },
- };
-
- case 'INSERT_BLOCKS':
- return {
- ...state,
- ...getFlattenedBlocksWithoutAttributes( action.blocks ),
- };
-
- case 'REPLACE_BLOCKS':
- if ( ! action.blocks ) {
- return state;
- }
-
- return {
- ...omit( state, action.clientIds ),
- ...getFlattenedBlocksWithoutAttributes( action.blocks ),
- };
-
- case 'REMOVE_BLOCKS':
- return omit( state, action.clientIds );
- }
-
- return state;
- },
-
- attributes( state = {}, action ) {
- switch ( action.type ) {
- case 'SETUP_EDITOR_STATE':
- return getFlattenedBlockAttributes( action.blocks );
-
- case 'RECEIVE_BLOCKS':
- return {
- ...state,
- ...getFlattenedBlockAttributes( action.blocks ),
- };
-
- case 'UPDATE_BLOCK':
- // Ignore updates if block isn't known or there are no attribute changes.
- if ( ! state[ action.clientId ] || ! action.updates.attributes ) {
- return state;
- }
-
- return {
- ...state,
- [ action.clientId ]: {
- ...state[ action.clientId ],
- ...action.updates.attributes,
- },
- };
-
- case 'UPDATE_BLOCK_ATTRIBUTES':
- // Ignore updates if block isn't known
- if ( ! state[ action.clientId ] ) {
- return state;
- }
-
- // Consider as updates only changed values
- const nextAttributes = reduce( action.attributes, ( result, value, key ) => {
- if ( value !== result[ key ] ) {
- result = getMutateSafeObject( state[ action.clientId ], result );
- result[ key ] = value;
- }
-
- return result;
- }, state[ action.clientId ] );
-
- // Skip update if nothing has been changed. The reference will
- // match the original block if `reduce` had no changed values.
- if ( nextAttributes === state[ action.clientId ] ) {
- return state;
- }
-
- // Otherwise replace attributes in state
- return {
- ...state,
- [ action.clientId ]: nextAttributes,
- };
-
- case 'INSERT_BLOCKS':
- return {
- ...state,
- ...getFlattenedBlockAttributes( action.blocks ),
- };
-
- case 'REPLACE_BLOCKS':
- if ( ! action.blocks ) {
- return state;
- }
-
- return {
- ...omit( state, action.clientIds ),
- ...getFlattenedBlockAttributes( action.blocks ),
- };
-
- case 'REMOVE_BLOCKS':
- return omit( state, action.clientIds );
- }
-
- return state;
- },
-
- order( state = {}, action ) {
- switch ( action.type ) {
- case 'SETUP_EDITOR_STATE':
- return mapBlockOrder( action.blocks );
-
- case 'RECEIVE_BLOCKS':
- return {
- ...state,
- ...omit( mapBlockOrder( action.blocks ), '' ),
- };
-
- case 'INSERT_BLOCKS': {
- const { rootClientId = '', blocks } = action;
- const subState = state[ rootClientId ] || [];
- const mappedBlocks = mapBlockOrder( blocks, rootClientId );
- const { index = subState.length } = action;
-
- return {
- ...state,
- ...mappedBlocks,
- [ rootClientId ]: insertAt( subState, mappedBlocks[ rootClientId ], index ),
- };
- }
-
- case 'MOVE_BLOCK_TO_POSITION': {
- const { fromRootClientId = '', toRootClientId = '', clientId } = action;
- const { index = state[ toRootClientId ].length } = action;
-
- // Moving inside the same parent block
- if ( fromRootClientId === toRootClientId ) {
- const subState = state[ toRootClientId ];
- const fromIndex = subState.indexOf( clientId );
- return {
- ...state,
- [ toRootClientId ]: moveTo( state[ toRootClientId ], fromIndex, index ),
- };
- }
-
- // Moving from a parent block to another
- return {
- ...state,
- [ fromRootClientId ]: without( state[ fromRootClientId ], clientId ),
- [ toRootClientId ]: insertAt( state[ toRootClientId ], clientId, index ),
- };
- }
-
- case 'MOVE_BLOCKS_UP': {
- const { clientIds, rootClientId = '' } = action;
- const firstClientId = first( clientIds );
- const subState = state[ rootClientId ];
-
- if ( ! subState.length || firstClientId === first( subState ) ) {
- return state;
- }
-
- const firstIndex = subState.indexOf( firstClientId );
-
- return {
- ...state,
- [ rootClientId ]: moveTo( subState, firstIndex, firstIndex - 1, clientIds.length ),
- };
- }
-
- case 'MOVE_BLOCKS_DOWN': {
- const { clientIds, rootClientId = '' } = action;
- const firstClientId = first( clientIds );
- const lastClientId = last( clientIds );
- const subState = state[ rootClientId ];
-
- if ( ! subState.length || lastClientId === last( subState ) ) {
- return state;
- }
-
- const firstIndex = subState.indexOf( firstClientId );
-
- return {
- ...state,
- [ rootClientId ]: moveTo( subState, firstIndex, firstIndex + 1, clientIds.length ),
- };
- }
-
- case 'REPLACE_BLOCKS': {
- const { blocks, clientIds } = action;
- if ( ! blocks ) {
- return state;
- }
-
- const mappedBlocks = mapBlockOrder( blocks );
-
- return flow( [
- ( nextState ) => omit( nextState, clientIds ),
- ( nextState ) => ( {
- ...nextState,
- ...omit( mappedBlocks, '' ),
- } ),
- ( nextState ) => mapValues( nextState, ( subState ) => (
- reduce( subState, ( result, clientId ) => {
- if ( clientId === clientIds[ 0 ] ) {
- return [
- ...result,
- ...mappedBlocks[ '' ],
- ];
- }
-
- if ( clientIds.indexOf( clientId ) === -1 ) {
- result.push( clientId );
- }
-
- return result;
- }, [] )
- ) ),
- ] )( state );
- }
-
- case 'REMOVE_BLOCKS':
- return flow( [
- // Remove inner block ordering for removed blocks
- ( nextState ) => omit( nextState, action.clientIds ),
-
- // Remove deleted blocks from other blocks' orderings
- ( nextState ) => mapValues( nextState, ( subState ) => (
- without( subState, ...action.clientIds )
- ) ),
- ] )( state );
- }
-
- return state;
- },
- } ),
} );
/**
@@ -967,26 +493,6 @@ export function template( state = { isValid: true }, action ) {
return state;
}
-/**
- * Reducer returning the editor setting.
- *
- * @param {Object} state Current state.
- * @param {Object} action Dispatched action.
- *
- * @return {Object} Updated state.
- */
-export function settings( state = EDITOR_SETTINGS_DEFAULTS, action ) {
- switch ( action.type ) {
- case 'UPDATE_EDITOR_SETTINGS':
- return {
- ...state,
- ...action.settings,
- };
- }
-
- return state;
-}
-
/**
* Reducer returning the user preferences.
*
@@ -997,35 +503,6 @@ export function settings( state = EDITOR_SETTINGS_DEFAULTS, action ) {
*/
export function preferences( state = PREFERENCES_DEFAULTS, action ) {
switch ( action.type ) {
- case 'INSERT_BLOCKS':
- case 'REPLACE_BLOCKS':
- return action.blocks.reduce( ( prevState, block ) => {
- let id = block.name;
- const insert = { name: block.name };
- if ( isReusableBlock( block ) ) {
- insert.ref = block.attributes.ref;
- id += '/' + block.attributes.ref;
- }
-
- return {
- ...prevState,
- insertUsage: {
- ...prevState.insertUsage,
- [ id ]: {
- time: action.time,
- count: prevState.insertUsage[ id ] ? prevState.insertUsage[ id ].count + 1 : 1,
- insert,
- },
- },
- };
- }, state );
-
- case 'REMOVE_REUSABLE_BLOCK':
- return {
- ...state,
- insertUsage: omitBy( state.insertUsage, ( { insert } ) => insert.ref === action.id ),
- };
-
case 'ENABLE_PUBLISH_SIDEBAR':
return {
...state,
@@ -1330,16 +807,29 @@ export function previewLink( state = null, action ) {
return state;
}
+/**
+ * Reducer returning whether the editor is ready to be rendered.
+ * The editor is considered ready to be rendered once
+ * the post object is loaded properly and the initial blocks parsed.
+ *
+ * @param {boolean} state
+ * @param {Object} action
+ *
+ * @return {boolean} Updated state.
+ */
+export function isReady( state = false, action ) {
+ switch ( action.type ) {
+ case 'SETUP_EDITOR_STATE':
+ return true;
+ }
+
+ return state;
+}
+
export default optimist( combineReducers( {
editor,
initialEdits,
currentPost,
- isTyping,
- isCaretWithinFormattedText,
- blockSelection,
- blocksMode,
- blockListSettings,
- insertionPoint,
preferences,
saving,
postLock,
@@ -1347,6 +837,6 @@ export default optimist( combineReducers( {
template,
autosave,
previewLink,
- settings,
postSavingLock,
+ isReady,
} ) );
diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js
index 7a1ab754c45a5..c3147abda7063 100644
--- a/packages/editor/src/store/selectors.js
+++ b/packages/editor/src/store/selectors.js
@@ -2,20 +2,10 @@
* External dependencies
*/
import {
- castArray,
- flatMap,
find,
- first,
get,
has,
- includes,
- isArray,
- isBoolean,
- last,
map,
- orderBy,
- reduce,
- some,
} from 'lodash';
import createSelector from 'rememo';
@@ -24,17 +14,14 @@ import createSelector from 'rememo';
*/
import {
serialize,
- getBlockType,
- getBlockTypes,
- hasBlockSupport,
- hasChildBlocksWithInserterSupport,
- getDefaultBlockName,
getFreeformContentHandlerName,
+ getDefaultBlockName,
isUnmodifiedDefaultBlock,
} from '@wordpress/blocks';
import { isInTheFuture, getDate } from '@wordpress/date';
import { removep } from '@wordpress/autop';
import { addQueryArgs } from '@wordpress/url';
+import { createRegistrySelector } from '@wordpress/data';
/**
* Internal dependencies
@@ -51,21 +38,16 @@ export const INSERTER_UTILITY_HIGH = 3;
export const INSERTER_UTILITY_MEDIUM = 2;
export const INSERTER_UTILITY_LOW = 1;
export const INSERTER_UTILITY_NONE = 0;
-const MILLISECONDS_PER_HOUR = 3600 * 1000;
-const MILLISECONDS_PER_DAY = 24 * 3600 * 1000;
-const MILLISECONDS_PER_WEEK = 7 * 24 * 3600 * 1000;
const ONE_MINUTE_IN_MS = 60 * 1000;
/**
- * Shared reference to an empty array for cases where it is important to avoid
- * returning a new array reference on every invocation, as in a connected or
+ * Shared reference to an empty object for cases where it is important to avoid
+ * returning a new object reference on every invocation, as in a connected or
* other pure component which performs `shouldComponentUpdate` check on props.
* This should be used as a last resort, since the normalized data should be
* maintained by the reducer result in state.
- *
- * @type {Array}
*/
-const EMPTY_ARRAY = [];
+const EMPTY_OBJECT = {};
/**
* Returns true if any past editor history snapshots exist, or false otherwise.
@@ -280,6 +262,34 @@ export function getCurrentPostAttribute( state, attributeName ) {
}
}
+/**
+ * Returns a single attribute of the post being edited, preferring the unsaved
+ * edit if one exists, but mergiging with the attribute value for the last known
+ * saved state of the post (this is needed for some nested attributes like meta).
+ *
+ * @param {Object} state Global application state.
+ * @param {string} attributeName Post attribute name.
+ *
+ * @return {*} Post attribute value.
+ */
+const getNestedEditedPostProperty = createSelector(
+ ( state, attributeName ) => {
+ const edits = getPostEdits( state );
+ if ( ! edits.hasOwnProperty( attributeName ) ) {
+ return getCurrentPostAttribute( state, attributeName );
+ }
+
+ return {
+ ...getCurrentPostAttribute( state, attributeName ),
+ ...edits[ attributeName ],
+ };
+ },
+ ( state, attributeName ) => [
+ get( state.editor.present.edits, [ attributeName ], EMPTY_OBJECT ),
+ get( state.currentPost, [ attributeName ], EMPTY_OBJECT ),
+ ]
+);
+
/**
* Returns a single attribute of the post being edited, preferring the unsaved
* edit if one exists, but falling back to the attribute for the last known
@@ -306,13 +316,7 @@ export function getEditedPostAttribute( state, attributeName ) {
// Merge properties are objects which contain only the patch edit in state,
// and thus must be merged with the current post attribute.
if ( EDIT_MERGE_PROPERTIES.has( attributeName ) ) {
- // [TODO]: Since this will return a new reference on each invocation,
- // consider caching in a way which would not impact non-merged property
- // derivation. Alternatively, introduce a new selector for meta lookup.
- return {
- ...getCurrentPostAttribute( state, attributeName ),
- ...edits[ attributeName ],
- };
+ return getNestedEditedPostProperty( state, attributeName );
}
return edits[ attributeName ];
@@ -461,12 +465,13 @@ export function isEditedPostEmpty( state ) {
// condition of the mere existence of blocks. Note that the value of edited
// content takes precedent over block content, and must fall through to the
// default logic.
- const rootClientIds = getBlockOrder( state );
- if ( rootClientIds.length && ! ( 'content' in getPostEdits( state ) ) ) {
+ const blocks = state.editor.present.blocks.value;
+
+ if ( blocks.length && ! ( 'content' in getPostEdits( state ) ) ) {
// Pierce the abstraction of the serializer in knowing that blocks are
// joined with with newlines such that even if every individual block
// produces an empty save result, the serialized content is non-empty.
- if ( rootClientIds.length > 1 ) {
+ if ( blocks.length > 1 ) {
return false;
}
@@ -481,7 +486,7 @@ export function isEditedPostEmpty( state ) {
//
// For all other content, the single block is assumed to make a post
// non-empty, if only by virtue of its own comment delimiters.
- const blockName = getBlockName( state, rootClientIds[ 0 ] );
+ const blockName = blocks[ 0 ].name;
if (
blockName !== getDefaultBlockName() &&
blockName !== getFreeformContentHandlerName()
@@ -590,1541 +595,268 @@ export function isEditedPostDateFloating( state ) {
}
/**
- * Returns a new reference when the inner blocks of a given block client ID
- * change. This is used exclusively as a memoized selector dependant, relying
- * on this selector's shared return value and recursively those of its inner
- * blocks defined as dependencies. This abuses mechanics of the selector
- * memoization to return from the original selector function only when
- * dependants change.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- *
- * @return {*} A value whose reference will change only when inner blocks of
- * the given block client ID change.
+ * Returns true if the post is currently being saved, or false otherwise.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {boolean} Whether post is being saved.
*/
-export const getBlockDependantsCacheBust = createSelector(
- () => [],
- ( state, clientId ) => map(
- getBlockOrder( state, clientId ),
- ( innerBlockClientId ) => getBlock( state, innerBlockClientId ),
- ),
-);
+export function isSavingPost( state ) {
+ return state.saving.requesting;
+}
/**
- * Returns a block's name given its client ID, or null if no block exists with
- * the client ID.
+ * Returns true if a previous post save was attempted successfully, or false
+ * otherwise.
*
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
+ * @param {Object} state Global application state.
*
- * @return {string} Block name.
+ * @return {boolean} Whether the post was saved successfully.
*/
-export function getBlockName( state, clientId ) {
- const block = state.editor.present.blocks.byClientId[ clientId ];
- return block ? block.name : null;
+export function didPostSaveRequestSucceed( state ) {
+ return state.saving.successful;
}
/**
- * Returns whether a block is valid or not.
+ * Returns true if a previous post save was attempted but failed, or false
+ * otherwise.
*
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
+ * @param {Object} state Global application state.
*
- * @return {boolean} Is Valid.
+ * @return {boolean} Whether the post save failed.
*/
-export function isBlockValid( state, clientId ) {
- const block = state.editor.present.blocks.byClientId[ clientId ];
- return !! block && block.isValid;
+export function didPostSaveRequestFail( state ) {
+ return !! state.saving.error;
}
/**
- * Returns a block's attributes given its client ID, or null if no block exists with
- * the client ID.
+ * Returns true if the post is autosaving, or false otherwise.
*
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
+ * @param {Object} state Global application state.
*
- * @return {Object?} Block attributes.
+ * @return {boolean} Whether the post is autosaving.
*/
-export const getBlockAttributes = createSelector(
- ( state, clientId ) => {
- const block = state.editor.present.blocks.byClientId[ clientId ];
- if ( ! block ) {
- return null;
- }
+export function isAutosavingPost( state ) {
+ return isSavingPost( state ) && !! state.saving.options.isAutosave;
+}
- let attributes = state.editor.present.blocks.attributes[ clientId ];
+/**
+ * Returns true if the post is being previewed, or false otherwise.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {boolean} Whether the post is being previewed.
+ */
+export function isPreviewingPost( state ) {
+ return isSavingPost( state ) && !! state.saving.options.isPreview;
+}
- // Inject custom source attribute values.
- //
- // TODO: Create generic external sourcing pattern, not explicitly
- // targeting meta attributes.
- const type = getBlockType( block.name );
- if ( type ) {
- attributes = reduce( type.attributes, ( result, value, key ) => {
- if ( value.source === 'meta' ) {
- if ( result === attributes ) {
- result = { ...result };
- }
-
- result[ key ] = getPostMeta( state, value.meta );
- }
-
- return result;
- }, attributes );
- }
+/**
+ * Returns the post preview link
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {string?} Preview Link.
+ */
+export function getEditedPostPreviewLink( state ) {
+ const featuredImageId = getEditedPostAttribute( state, 'featured_media' );
+ const previewLink = state.previewLink;
+ if ( previewLink && featuredImageId ) {
+ return addQueryArgs( previewLink, { _thumbnail_id: featuredImageId } );
+ }
- return attributes;
- },
- ( state, clientId ) => [
- state.editor.present.blocks.byClientId[ clientId ],
- state.editor.present.blocks.attributes[ clientId ],
- state.editor.present.edits.meta,
- state.initialEdits.meta,
- state.currentPost.meta,
- ]
-);
+ return previewLink;
+}
/**
- * Returns a block given its client ID. This is a parsed copy of the block,
- * containing its `blockName`, `clientId`, and current `attributes` state. This
- * is not the block's registration settings, which must be retrieved from the
- * blocks module registration store.
+ * Returns a suggested post format for the current post, inferred only if there
+ * is a single block within the post and it is of a type known to match a
+ * default post format. Returns null if the format cannot be determined.
*
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
+ * @param {Object} state Global application state.
*
- * @return {Object} Parsed block object.
+ * @return {?string} Suggested post format.
*/
-export const getBlock = createSelector(
- ( state, clientId ) => {
- const block = state.editor.present.blocks.byClientId[ clientId ];
- if ( ! block ) {
- return null;
- }
+export function getSuggestedPostFormat( state ) {
+ const blocks = state.editor.present.blocks.value;
- return {
- ...block,
- attributes: getBlockAttributes( state, clientId ),
- innerBlocks: getBlocks( state, clientId ),
- };
- },
- ( state, clientId ) => [
- ...getBlockAttributes.getDependants( state, clientId ),
- getBlockDependantsCacheBust( state, clientId ),
- ]
-);
+ let name;
+ // If there is only one block in the content of the post grab its name
+ // so we can derive a suitable post format from it.
+ if ( blocks.length === 1 ) {
+ name = blocks[ 0 ].name;
+ }
-export const __unstableGetBlockWithoutInnerBlocks = createSelector(
- ( state, clientId ) => {
- const block = state.editor.present.blocks.byClientId[ clientId ];
- if ( ! block ) {
- return null;
+ // If there are two blocks in the content and the last one is a text blocks
+ // grab the name of the first one to also suggest a post format from it.
+ if ( blocks.length === 2 ) {
+ if ( blocks[ 1 ].name === 'core/paragraph' ) {
+ name = blocks[ 0 ].name;
}
+ }
- return {
- ...block,
- attributes: getBlockAttributes( state, clientId ),
- };
- },
- ( state, clientId ) => [
- state.editor.present.blocks.byClientId[ clientId ],
- ...getBlockAttributes.getDependants( state, clientId ),
- ]
-);
+ // We only convert to default post formats in core.
+ switch ( name ) {
+ case 'core/image':
+ return 'image';
+ case 'core/quote':
+ case 'core/pullquote':
+ return 'quote';
+ case 'core/gallery':
+ return 'gallery';
+ case 'core/video':
+ case 'core-embed/youtube':
+ case 'core-embed/vimeo':
+ return 'video';
+ case 'core/audio':
+ case 'core-embed/spotify':
+ case 'core-embed/soundcloud':
+ return 'audio';
+ }
-function getPostMeta( state, key ) {
- return has( state, [ 'editor', 'present', 'edits', 'meta', key ] ) ?
- get( state, [ 'editor', 'present', 'edits', 'meta', key ] ) :
- get( state, [ 'currentPost', 'meta', key ] );
+ return null;
}
/**
- * Returns all block objects for the current post being edited as an array in
- * the order they appear in the post.
- *
- * Note: It's important to memoize this selector to avoid return a new instance
- * on each call
+ * Returns a set of blocks which are to be used in consideration of the post's
+ * generated save content.
*
- * @param {Object} state Editor state.
- * @param {?String} rootClientId Optional root client ID of block list.
+ * @param {Object} state Editor state.
*
- * @return {Object[]} Post blocks.
+ * @return {WPBlock[]} Filtered set of blocks for save.
*/
-export const getBlocks = createSelector(
- ( state, rootClientId ) => {
- return map(
- getBlockOrder( state, rootClientId ),
- ( clientId ) => getBlock( state, clientId )
- );
- },
- ( state ) => [ state.editor.present.blocks ]
-);
+export function getBlocksForSerialization( state ) {
+ const blocks = state.editor.present.blocks.value;
-/**
- * Returns an array containing the clientIds of all descendants
- * of the blocks given.
- *
- * @param {Object} state Global application state.
- * @param {Array} clientIds Array of blocks to inspect.
- *
- * @return {Array} ids of descendants.
- */
-export const getClientIdsOfDescendants = ( state, clientIds ) => flatMap( clientIds, ( clientId ) => {
- const descendants = getBlockOrder( state, clientId );
- return [ ...descendants, ...getClientIdsOfDescendants( state, descendants ) ];
-} );
+ // WARNING: Any changes to the logic of this function should be verified
+ // against the implementation of isEditedPostEmpty, which bypasses this
+ // function for performance' sake, in an assumption of this current logic
+ // being irrelevant to the optimized condition of emptiness.
+
+ // A single unmodified default block is assumed to be equivalent to an
+ // empty post.
+ const isSingleUnmodifiedDefaultBlock = (
+ blocks.length === 1 &&
+ isUnmodifiedDefaultBlock( blocks[ 0 ] )
+ );
+
+ if ( isSingleUnmodifiedDefaultBlock ) {
+ return [];
+ }
+
+ return blocks;
+}
/**
- * Returns an array containing the clientIds of the top-level blocks
- * and their descendants of any depth (for nested blocks).
+ * Returns the content of the post being edited, preferring raw string edit
+ * before falling back to serialization of block state.
*
* @param {Object} state Global application state.
*
- * @return {Array} ids of top-level and descendant blocks.
+ * @return {string} Post content.
*/
-export const getClientIdsWithDescendants = createSelector(
+export const getEditedPostContent = createSelector(
( state ) => {
- const topLevelIds = getBlockOrder( state );
- return [ ...topLevelIds, ...getClientIdsOfDescendants( state, topLevelIds ) ];
- },
- ( state ) => [
- state.editor.present.blocks.order,
- ]
-);
+ const edits = getPostEdits( state );
+ if ( 'content' in edits ) {
+ return edits.content;
+ }
-/**
- * Returns the total number of blocks, or the total number of blocks with a specific name in a post.
- * The number returned includes nested blocks.
- *
- * @param {Object} state Global application state.
- * @param {?String} blockName Optional block name, if specified only blocks of that type will be counted.
- *
- * @return {number} Number of blocks in the post, or number of blocks with name equal to blockName.
- */
-export const getGlobalBlockCount = createSelector(
- ( state, blockName ) => {
- const clientIds = getClientIdsWithDescendants( state );
- if ( ! blockName ) {
- return clientIds.length;
+ const blocks = getBlocksForSerialization( state );
+ const content = serialize( blocks );
+
+ // For compatibility purposes, treat a post consisting of a single
+ // freeform block as legacy content and downgrade to a pre-block-editor
+ // removep'd content format.
+ const isSingleFreeformBlock = (
+ blocks.length === 1 &&
+ blocks[ 0 ].name === getFreeformContentHandlerName()
+ );
+
+ if ( isSingleFreeformBlock ) {
+ return removep( content );
}
- return reduce( clientIds, ( count, clientId ) => {
- const block = state.editor.present.blocks.byClientId[ clientId ];
- return block.name === blockName ? count + 1 : count;
- }, 0 );
+
+ return content;
},
( state ) => [
- state.editor.present.blocks.order,
- state.editor.present.blocks.byClientId,
- ]
+ state.editor.present.blocks.value,
+ state.editor.present.edits.content,
+ state.initialEdits.content,
+ ],
);
/**
- * Given an array of block client IDs, returns the corresponding array of block
- * objects.
+ * Returns the reusable block with the given ID.
*
- * @param {Object} state Editor state.
- * @param {string[]} clientIds Client IDs for which blocks are to be returned.
+ * @param {Object} state Global application state.
+ * @param {number|string} ref The reusable block's ID.
*
- * @return {WPBlock[]} Block objects.
+ * @return {Object} The reusable block, or null if none exists.
*/
-export const getBlocksByClientId = createSelector(
- ( state, clientIds ) => map(
- castArray( clientIds ),
- ( clientId ) => getBlock( state, clientId )
- ),
- ( state ) => [
- state.editor.present.edits.meta,
- state.initialEdits.meta,
- state.currentPost.meta,
- state.editor.present.blocks,
- ]
+export const __experimentalGetReusableBlock = createSelector(
+ ( state, ref ) => {
+ const block = state.reusableBlocks.data[ ref ];
+ if ( ! block ) {
+ return null;
+ }
+
+ const isTemporary = isNaN( parseInt( ref ) );
+
+ return {
+ ...block,
+ id: isTemporary ? ref : +ref,
+ isTemporary,
+ };
+ },
+ ( state, ref ) => [
+ state.reusableBlocks.data[ ref ],
+ ],
);
/**
- * Returns the number of blocks currently present in the post.
+ * Returns whether or not the reusable block with the given ID is being saved.
*
- * @param {Object} state Editor state.
- * @param {?string} rootClientId Optional root client ID of block list.
+ * @param {Object} state Global application state.
+ * @param {string} ref The reusable block's ID.
*
- * @return {number} Number of blocks in the post.
+ * @return {boolean} Whether or not the reusable block is being saved.
*/
-export function getBlockCount( state, rootClientId ) {
- return getBlockOrder( state, rootClientId ).length;
+export function __experimentalIsSavingReusableBlock( state, ref ) {
+ return state.reusableBlocks.isSaving[ ref ] || false;
}
/**
- * Returns the current block selection start. This value may be null, and it
- * may represent either a singular block selection or multi-selection start.
- * A selection is singular if its start and end match.
+ * Returns true if the reusable block with the given ID is being fetched, or
+ * false otherwise.
*
* @param {Object} state Global application state.
+ * @param {string} ref The reusable block's ID.
*
- * @return {?string} Client ID of block selection start.
+ * @return {boolean} Whether the reusable block is being fetched.
*/
-export function getBlockSelectionStart( state ) {
- return state.blockSelection.start;
+export function __experimentalIsFetchingReusableBlock( state, ref ) {
+ return !! state.reusableBlocks.isFetching[ ref ];
}
/**
- * Returns the current block selection end. This value may be null, and it
- * may represent either a singular block selection or multi-selection end.
- * A selection is singular if its start and end match.
+ * Returns an array of all reusable blocks.
*
* @param {Object} state Global application state.
*
- * @return {?string} Client ID of block selection end.
+ * @return {Array} An array of all reusable blocks.
*/
-export function getBlockSelectionEnd( state ) {
- return state.blockSelection.end;
-}
-
-/**
- * Returns the number of blocks currently selected in the post.
- *
- * @param {Object} state Global application state.
- *
- * @return {number} Number of blocks selected in the post.
- */
-export function getSelectedBlockCount( state ) {
- const multiSelectedBlockCount = getMultiSelectedBlockClientIds( state ).length;
-
- if ( multiSelectedBlockCount ) {
- return multiSelectedBlockCount;
- }
-
- return state.blockSelection.start ? 1 : 0;
-}
-
-/**
- * Returns true if there is a single selected block, or false otherwise.
- *
- * @param {Object} state Editor state.
- *
- * @return {boolean} Whether a single block is selected.
- */
-export function hasSelectedBlock( state ) {
- const { start, end } = state.blockSelection;
- return !! start && start === end;
-}
-
-/**
- * Returns the currently selected block client ID, or null if there is no
- * selected block.
- *
- * @param {Object} state Editor state.
- *
- * @return {?string} Selected block client ID.
- */
-export function getSelectedBlockClientId( state ) {
- const { start, end } = state.blockSelection;
- // We need to check the block exists because the current state.blockSelection reducer
- // doesn't take into account the UNDO / REDO actions to update selection.
- // To be removed when that's fixed.
- return start && start === end && !! state.editor.present.blocks.byClientId[ start ] ? start : null;
-}
-
-/**
- * Returns the currently selected block, or null if there is no selected block.
- *
- * @param {Object} state Global application state.
- *
- * @return {?Object} Selected block.
- */
-export function getSelectedBlock( state ) {
- const clientId = getSelectedBlockClientId( state );
- return clientId ? getBlock( state, clientId ) : null;
-}
-
-/**
- * Given a block client ID, returns the root block from which the block is
- * nested, an empty string for top-level blocks, or null if the block does not
- * exist.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block from which to find root client ID.
- *
- * @return {?string} Root client ID, if exists
- */
-export const getBlockRootClientId = createSelector(
- ( state, clientId ) => {
- const { order } = state.editor.present.blocks;
-
- for ( const rootClientId in order ) {
- if ( includes( order[ rootClientId ], clientId ) ) {
- return rootClientId;
- }
- }
-
- return null;
- },
- ( state ) => [
- state.editor.present.blocks.order,
- ]
-);
-
-/**
- * Given a block client ID, returns the root of the hierarchy from which the block is nested, return the block itself for root level blocks.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block from which to find root client ID.
- *
- * @return {string} Root client ID
- */
-export const getBlockHierarchyRootClientId = createSelector(
- ( state, clientId ) => {
- let rootClientId = clientId;
- let current = clientId;
- while ( rootClientId ) {
- current = rootClientId;
- rootClientId = getBlockRootClientId( state, current );
- }
-
- return current;
- },
- ( state ) => [
- state.editor.present.blocks.order,
- ]
-);
-
-/**
- * Returns the client ID of the block adjacent one at the given reference
- * startClientId and modifier directionality. Defaults start startClientId to
- * the selected block, and direction as next block. Returns null if there is no
- * adjacent block.
- *
- * @param {Object} state Editor state.
- * @param {?string} startClientId Optional client ID of block from which to
- * search.
- * @param {?number} modifier Directionality multiplier (1 next, -1
- * previous).
- *
- * @return {?string} Return the client ID of the block, or null if none exists.
- */
-export function getAdjacentBlockClientId( state, startClientId, modifier = 1 ) {
- // Default to selected block.
- if ( startClientId === undefined ) {
- startClientId = getSelectedBlockClientId( state );
- }
-
- // Try multi-selection starting at extent based on modifier.
- if ( startClientId === undefined ) {
- if ( modifier < 0 ) {
- startClientId = getFirstMultiSelectedBlockClientId( state );
- } else {
- startClientId = getLastMultiSelectedBlockClientId( state );
- }
- }
-
- // Validate working start client ID.
- if ( ! startClientId ) {
- return null;
- }
-
- // Retrieve start block root client ID, being careful to allow the falsey
- // empty string top-level root by explicitly testing against null.
- const rootClientId = getBlockRootClientId( state, startClientId );
- if ( rootClientId === null ) {
- return null;
- }
-
- const { order } = state.editor.present.blocks;
- const orderSet = order[ rootClientId ];
- const index = orderSet.indexOf( startClientId );
- const nextIndex = ( index + ( 1 * modifier ) );
-
- // Block was first in set and we're attempting to get previous.
- if ( nextIndex < 0 ) {
- return null;
- }
-
- // Block was last in set and we're attempting to get next.
- if ( nextIndex === orderSet.length ) {
- return null;
- }
-
- // Assume incremented index is within the set.
- return orderSet[ nextIndex ];
-}
-
-/**
- * Returns the previous block's client ID from the given reference start ID.
- * Defaults start to the selected block. Returns null if there is no previous
- * block.
- *
- * @param {Object} state Editor state.
- * @param {?string} startClientId Optional client ID of block from which to
- * search.
- *
- * @return {?string} Adjacent block's client ID, or null if none exists.
- */
-export function getPreviousBlockClientId( state, startClientId ) {
- return getAdjacentBlockClientId( state, startClientId, -1 );
-}
-
-/**
- * Returns the next block's client ID from the given reference start ID.
- * Defaults start to the selected block. Returns null if there is no next
- * block.
- *
- * @param {Object} state Editor state.
- * @param {?string} startClientId Optional client ID of block from which to
- * search.
- *
- * @return {?string} Adjacent block's client ID, or null if none exists.
- */
-export function getNextBlockClientId( state, startClientId ) {
- return getAdjacentBlockClientId( state, startClientId, 1 );
-}
-
-/**
- * Returns the initial caret position for the selected block.
- * This position is to used to position the caret properly when the selected block changes.
- *
- * @param {Object} state Global application state.
- *
- * @return {?Object} Selected block.
- */
-export function getSelectedBlocksInitialCaretPosition( state ) {
- const { start, end } = state.blockSelection;
- if ( start !== end || ! start ) {
- return null;
- }
-
- return state.blockSelection.initialPosition;
-}
-
-/**
- * Returns the current multi-selection set of block client IDs, or an empty
- * array if there is no multi-selection.
- *
- * @param {Object} state Editor state.
- *
- * @return {Array} Multi-selected block client IDs.
- */
-export const getMultiSelectedBlockClientIds = createSelector(
- ( state ) => {
- const { start, end } = state.blockSelection;
- if ( start === end ) {
- return [];
- }
-
- // Retrieve root client ID to aid in retrieving relevant nested block
- // order, being careful to allow the falsey empty string top-level root
- // by explicitly testing against null.
- const rootClientId = getBlockRootClientId( state, start );
- if ( rootClientId === null ) {
- return [];
- }
-
- const blockOrder = getBlockOrder( state, rootClientId );
- const startIndex = blockOrder.indexOf( start );
- const endIndex = blockOrder.indexOf( end );
-
- if ( startIndex > endIndex ) {
- return blockOrder.slice( endIndex, startIndex + 1 );
- }
-
- return blockOrder.slice( startIndex, endIndex + 1 );
- },
- ( state ) => [
- state.editor.present.blocks.order,
- state.blockSelection.start,
- state.blockSelection.end,
- ],
-);
-
-/**
- * Returns the current multi-selection set of blocks, or an empty array if
- * there is no multi-selection.
- *
- * @param {Object} state Editor state.
- *
- * @return {Array} Multi-selected block objects.
- */
-export const getMultiSelectedBlocks = createSelector(
- ( state ) => {
- const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds( state );
- if ( ! multiSelectedBlockClientIds.length ) {
- return EMPTY_ARRAY;
- }
-
- return multiSelectedBlockClientIds.map( ( clientId ) => getBlock( state, clientId ) );
- },
- ( state ) => [
- ...getMultiSelectedBlockClientIds.getDependants( state ),
- state.editor.present.blocks,
- state.editor.present.edits.meta,
- state.initialEdits.meta,
- state.currentPost.meta,
- ]
-);
-
-/**
- * Returns the client ID of the first block in the multi-selection set, or null
- * if there is no multi-selection.
- *
- * @param {Object} state Editor state.
- *
- * @return {?string} First block client ID in the multi-selection set.
- */
-export function getFirstMultiSelectedBlockClientId( state ) {
- return first( getMultiSelectedBlockClientIds( state ) ) || null;
-}
-
-/**
- * Returns the client ID of the last block in the multi-selection set, or null
- * if there is no multi-selection.
- *
- * @param {Object} state Editor state.
- *
- * @return {?string} Last block client ID in the multi-selection set.
- */
-export function getLastMultiSelectedBlockClientId( state ) {
- return last( getMultiSelectedBlockClientIds( state ) ) || null;
-}
-
-/**
- * Checks if possibleAncestorId is an ancestor of possibleDescendentId.
- *
- * @param {Object} state Editor state.
- * @param {string} possibleAncestorId Possible ancestor client ID.
- * @param {string} possibleDescendentId Possible descent client ID.
- *
- * @return {boolean} True if possibleAncestorId is an ancestor
- * of possibleDescendentId, and false otherwise.
- */
-const isAncestorOf = createSelector(
- ( state, possibleAncestorId, possibleDescendentId ) => {
- let idToCheck = possibleDescendentId;
- while ( possibleAncestorId !== idToCheck && idToCheck ) {
- idToCheck = getBlockRootClientId( state, idToCheck );
- }
- return possibleAncestorId === idToCheck;
- },
- ( state ) => [
- state.editor.present.blocks.order,
- ],
-);
-
-/**
- * Returns true if a multi-selection exists, and the block corresponding to the
- * specified client ID is the first block of the multi-selection set, or false
- * otherwise.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- *
- * @return {boolean} Whether block is first in multi-selection.
- */
-export function isFirstMultiSelectedBlock( state, clientId ) {
- return getFirstMultiSelectedBlockClientId( state ) === clientId;
-}
-
-/**
- * Returns true if the client ID occurs within the block multi-selection, or
- * false otherwise.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- *
- * @return {boolean} Whether block is in multi-selection set.
- */
-export function isBlockMultiSelected( state, clientId ) {
- return getMultiSelectedBlockClientIds( state ).indexOf( clientId ) !== -1;
-}
-
-/**
- * Returns true if an ancestor of the block is multi-selected, or false
- * otherwise.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- *
- * @return {boolean} Whether an ancestor of the block is in multi-selection
- * set.
- */
-export const isAncestorMultiSelected = createSelector(
- ( state, clientId ) => {
- let ancestorClientId = clientId;
- let isMultiSelected = false;
- while ( ancestorClientId && ! isMultiSelected ) {
- ancestorClientId = getBlockRootClientId( state, ancestorClientId );
- isMultiSelected = isBlockMultiSelected( state, ancestorClientId );
- }
- return isMultiSelected;
- },
- ( state ) => [
- state.editor.present.blocks.order,
- state.blockSelection.start,
- state.blockSelection.end,
- ],
-);
-/**
- * Returns the client ID of the block which begins the multi-selection set, or
- * null if there is no multi-selection.
- *
- * This is not necessarily the first client ID in the selection.
- *
- * @see getFirstMultiSelectedBlockClientId
- *
- * @param {Object} state Editor state.
- *
- * @return {?string} Client ID of block beginning multi-selection.
- */
-export function getMultiSelectedBlocksStartClientId( state ) {
- const { start, end } = state.blockSelection;
- if ( start === end ) {
- return null;
- }
- return start || null;
-}
-
-/**
- * Returns the client ID of the block which ends the multi-selection set, or
- * null if there is no multi-selection.
- *
- * This is not necessarily the last client ID in the selection.
- *
- * @see getLastMultiSelectedBlockClientId
- *
- * @param {Object} state Editor state.
- *
- * @return {?string} Client ID of block ending multi-selection.
- */
-export function getMultiSelectedBlocksEndClientId( state ) {
- const { start, end } = state.blockSelection;
- if ( start === end ) {
- return null;
- }
- return end || null;
-}
-
-/**
- * Returns an array containing all block client IDs in the editor in the order
- * they appear. Optionally accepts a root client ID of the block list for which
- * the order should be returned, defaulting to the top-level block order.
- *
- * @param {Object} state Editor state.
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {Array} Ordered client IDs of editor blocks.
- */
-export function getBlockOrder( state, rootClientId ) {
- return state.editor.present.blocks.order[ rootClientId || '' ] || EMPTY_ARRAY;
-}
-
-/**
- * Returns the index at which the block corresponding to the specified client
- * ID occurs within the block order, or `-1` if the block does not exist.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {number} Index at which block exists in order.
- */
-export function getBlockIndex( state, clientId, rootClientId ) {
- return getBlockOrder( state, rootClientId ).indexOf( clientId );
-}
-
-/**
- * Returns true if the block corresponding to the specified client ID is
- * currently selected and no multi-selection exists, or false otherwise.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- *
- * @return {boolean} Whether block is selected and multi-selection exists.
- */
-export function isBlockSelected( state, clientId ) {
- const { start, end } = state.blockSelection;
-
- if ( start !== end ) {
- return false;
- }
-
- return start === clientId;
-}
-
-/**
- * Returns true if one of the block's inner blocks is selected.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- * @param {boolean} deep Perform a deep check.
- *
- * @return {boolean} Whether the block as an inner block selected
- */
-export function hasSelectedInnerBlock( state, clientId, deep = false ) {
- return some(
- getBlockOrder( state, clientId ),
- ( innerClientId ) => (
- isBlockSelected( state, innerClientId ) ||
- isBlockMultiSelected( state, innerClientId ) ||
- ( deep && hasSelectedInnerBlock( state, innerClientId, deep ) )
- )
- );
-}
-
-/**
- * Returns true if the block corresponding to the specified client ID is
- * currently selected but isn't the last of the selected blocks. Here "last"
- * refers to the block sequence in the document, _not_ the sequence of
- * multi-selection, which is why `state.blockSelection.end` isn't used.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- *
- * @return {boolean} Whether block is selected and not the last in the
- * selection.
- */
-export function isBlockWithinSelection( state, clientId ) {
- if ( ! clientId ) {
- return false;
- }
-
- const clientIds = getMultiSelectedBlockClientIds( state );
- const index = clientIds.indexOf( clientId );
- return index > -1 && index < clientIds.length - 1;
-}
-
-/**
- * Returns true if a multi-selection has been made, or false otherwise.
- *
- * @param {Object} state Editor state.
- *
- * @return {boolean} Whether multi-selection has been made.
- */
-export function hasMultiSelection( state ) {
- const { start, end } = state.blockSelection;
- return start !== end;
-}
-
-/**
- * Whether in the process of multi-selecting or not. This flag is only true
- * while the multi-selection is being selected (by mouse move), and is false
- * once the multi-selection has been settled.
- *
- * @see hasMultiSelection
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} True if multi-selecting, false if not.
- */
-export function isMultiSelecting( state ) {
- return state.blockSelection.isMultiSelecting;
-}
-
-/**
- * Selector that returns if multi-selection is enabled or not.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} True if it should be possible to multi-select blocks, false if multi-selection is disabled.
- */
-export function isSelectionEnabled( state ) {
- return state.blockSelection.isEnabled;
-}
-
-/**
- * Returns the block's editing mode, defaulting to "visual" if not explicitly
- * assigned.
- *
- * @param {Object} state Editor state.
- * @param {string} clientId Block client ID.
- *
- * @return {Object} Block editing mode.
- */
-export function getBlockMode( state, clientId ) {
- return state.blocksMode[ clientId ] || 'visual';
-}
-
-/**
- * Returns true if the user is typing, or false otherwise.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} Whether user is typing.
- */
-export function isTyping( state ) {
- return state.isTyping;
-}
-
-/**
- * Returns true if the caret is within formatted text, or false otherwise.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} Whether the caret is within formatted text.
- */
-export function isCaretWithinFormattedText( state ) {
- return state.isCaretWithinFormattedText;
-}
-
-/**
- * Returns the insertion point, the index at which the new inserted block would
- * be placed. Defaults to the last index.
- *
- * @param {Object} state Editor state.
- *
- * @return {Object} Insertion point object with `rootClientId`, `index`.
- */
-export function getBlockInsertionPoint( state ) {
- let rootClientId, index;
-
- const { insertionPoint, blockSelection } = state;
- if ( insertionPoint !== null ) {
- return insertionPoint;
- }
-
- const { end } = blockSelection;
- if ( end ) {
- rootClientId = getBlockRootClientId( state, end ) || undefined;
- index = getBlockIndex( state, end, rootClientId ) + 1;
- } else {
- index = getBlockOrder( state ).length;
- }
-
- return { rootClientId, index };
-}
-
-/**
- * Returns true if we should show the block insertion point.
- *
- * @param {Object} state Global application state.
- *
- * @return {?boolean} Whether the insertion point is visible or not.
- */
-export function isBlockInsertionPointVisible( state ) {
- return state.insertionPoint !== null;
-}
-
-/**
- * Returns whether the blocks matches the template or not.
- *
- * @param {boolean} state
- * @return {?boolean} Whether the template is valid or not.
- */
-export function isValidTemplate( state ) {
- return state.template.isValid;
-}
-
-/**
- * Returns the defined block template
- *
- * @param {boolean} state
- * @return {?Array} Block Template
- */
-export function getTemplate( state ) {
- return state.settings.template;
-}
-
-/**
- * Returns the defined block template lock. Optionally accepts a root block
- * client ID as context, otherwise defaulting to the global context.
- *
- * @param {Object} state Editor state.
- * @param {?string} rootClientId Optional block root client ID.
- *
- * @return {?string} Block Template Lock
- */
-export function getTemplateLock( state, rootClientId ) {
- if ( ! rootClientId ) {
- return state.settings.templateLock;
- }
-
- const blockListSettings = getBlockListSettings( state, rootClientId );
- if ( ! blockListSettings ) {
- return null;
- }
-
- return blockListSettings.templateLock;
-}
-
-/**
- * Returns true if the post is currently being saved, or false otherwise.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} Whether post is being saved.
- */
-export function isSavingPost( state ) {
- return state.saving.requesting;
-}
-
-/**
- * Returns true if a previous post save was attempted successfully, or false
- * otherwise.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} Whether the post was saved successfully.
- */
-export function didPostSaveRequestSucceed( state ) {
- return state.saving.successful;
-}
-
-/**
- * Returns true if a previous post save was attempted but failed, or false
- * otherwise.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} Whether the post save failed.
- */
-export function didPostSaveRequestFail( state ) {
- return !! state.saving.error;
-}
-
-/**
- * Returns true if the post is autosaving, or false otherwise.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} Whether the post is autosaving.
- */
-export function isAutosavingPost( state ) {
- return isSavingPost( state ) && !! state.saving.options.isAutosave;
-}
-
-/**
- * Returns true if the post is being previewed, or false otherwise.
- *
- * @param {Object} state Global application state.
- *
- * @return {boolean} Whether the post is being previewed.
- */
-export function isPreviewingPost( state ) {
- return isSavingPost( state ) && !! state.saving.options.isPreview;
-}
-
-/**
- * Returns the post preview link
- *
- * @param {Object} state Global application state.
- *
- * @return {string?} Preview Link.
- */
-export function getEditedPostPreviewLink( state ) {
- const featuredImageId = getEditedPostAttribute( state, 'featured_media' );
- const previewLink = state.previewLink;
- if ( previewLink && featuredImageId ) {
- return addQueryArgs( previewLink, { _thumbnail_id: featuredImageId } );
- }
-
- return previewLink;
-}
-
-/**
- * Returns a suggested post format for the current post, inferred only if there
- * is a single block within the post and it is of a type known to match a
- * default post format. Returns null if the format cannot be determined.
- *
- * @param {Object} state Global application state.
- *
- * @return {?string} Suggested post format.
- */
-export function getSuggestedPostFormat( state ) {
- const blocks = getBlockOrder( state );
-
- let name;
- // If there is only one block in the content of the post grab its name
- // so we can derive a suitable post format from it.
- if ( blocks.length === 1 ) {
- name = getBlockName( state, blocks[ 0 ] );
- }
-
- // If there are two blocks in the content and the last one is a text blocks
- // grab the name of the first one to also suggest a post format from it.
- if ( blocks.length === 2 ) {
- if ( getBlockName( state, blocks[ 1 ] ) === 'core/paragraph' ) {
- name = getBlockName( state, blocks[ 0 ] );
- }
- }
-
- // We only convert to default post formats in core.
- switch ( name ) {
- case 'core/image':
- return 'image';
- case 'core/quote':
- case 'core/pullquote':
- return 'quote';
- case 'core/gallery':
- return 'gallery';
- case 'core/video':
- case 'core-embed/youtube':
- case 'core-embed/vimeo':
- return 'video';
- case 'core/audio':
- case 'core-embed/spotify':
- case 'core-embed/soundcloud':
- return 'audio';
- }
-
- return null;
-}
-
-/**
- * Returns a set of blocks which are to be used in consideration of the post's
- * generated save content.
- *
- * @param {Object} state Editor state.
- *
- * @return {WPBlock[]} Filtered set of blocks for save.
- */
-export function getBlocksForSerialization( state ) {
- const blocks = getBlocks( state );
-
- // WARNING: Any changes to the logic of this function should be verified
- // against the implementation of isEditedPostEmpty, which bypasses this
- // function for performance' sake, in an assumption of this current logic
- // being irrelevant to the optimized condition of emptiness.
-
- // A single unmodified default block is assumed to be equivalent to an
- // empty post.
- const isSingleUnmodifiedDefaultBlock = (
- blocks.length === 1 &&
- isUnmodifiedDefaultBlock( blocks[ 0 ] )
- );
-
- if ( isSingleUnmodifiedDefaultBlock ) {
- return [];
- }
-
- return blocks;
-}
-
-/**
- * Returns the content of the post being edited, preferring raw string edit
- * before falling back to serialization of block state.
- *
- * @param {Object} state Global application state.
- *
- * @return {string} Post content.
- */
-export const getEditedPostContent = createSelector(
- ( state ) => {
- const edits = getPostEdits( state );
- if ( 'content' in edits ) {
- return edits.content;
- }
-
- const blocks = getBlocksForSerialization( state );
- const content = serialize( blocks );
-
- // For compatibility purposes, treat a post consisting of a single
- // freeform block as legacy content and downgrade to a pre-block-editor
- // removep'd content format.
- const isSingleFreeformBlock = (
- blocks.length === 1 &&
- blocks[ 0 ].name === getFreeformContentHandlerName()
- );
-
- if ( isSingleFreeformBlock ) {
- return removep( content );
- }
-
- return content;
- },
- ( state ) => [
- state.editor.present.blocks,
- state.editor.present.edits.content,
- state.initialEdits.content,
- ],
-);
-
-/**
- * Determines if the given block type is allowed to be inserted into the block list.
- * This function is not exported and not memoized because using a memoized selector
- * inside another memoized selector is just a waste of time.
- *
- * @param {Object} state Editor state.
- * @param {string} blockName The name of the block type, e.g.' core/paragraph'.
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {boolean} Whether the given block type is allowed to be inserted.
- */
-const canInsertBlockTypeUnmemoized = ( state, blockName, rootClientId = null ) => {
- const checkAllowList = ( list, item, defaultResult = null ) => {
- if ( isBoolean( list ) ) {
- return list;
- }
- if ( isArray( list ) ) {
- return includes( list, item );
- }
- return defaultResult;
- };
-
- const blockType = getBlockType( blockName );
- if ( ! blockType ) {
- return false;
- }
-
- const { allowedBlockTypes } = getEditorSettings( state );
-
- const isBlockAllowedInEditor = checkAllowList( allowedBlockTypes, blockName, true );
- if ( ! isBlockAllowedInEditor ) {
- return false;
- }
-
- const isLocked = !! getTemplateLock( state, rootClientId );
- if ( isLocked ) {
- return false;
- }
-
- const parentBlockListSettings = getBlockListSettings( state, rootClientId );
- const parentAllowedBlocks = get( parentBlockListSettings, [ 'allowedBlocks' ] );
- const hasParentAllowedBlock = checkAllowList( parentAllowedBlocks, blockName );
-
- const blockAllowedParentBlocks = blockType.parent;
- const parentName = getBlockName( state, rootClientId );
- const hasBlockAllowedParent = checkAllowList( blockAllowedParentBlocks, parentName );
-
- if ( hasParentAllowedBlock !== null && hasBlockAllowedParent !== null ) {
- return hasParentAllowedBlock || hasBlockAllowedParent;
- } else if ( hasParentAllowedBlock !== null ) {
- return hasParentAllowedBlock;
- } else if ( hasBlockAllowedParent !== null ) {
- return hasBlockAllowedParent;
- }
-
- return true;
-};
-
-/**
- * Determines if the given block type is allowed to be inserted into the block list.
- *
- * @param {Object} state Editor state.
- * @param {string} blockName The name of the block type, e.g.' core/paragraph'.
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {boolean} Whether the given block type is allowed to be inserted.
- */
-export const canInsertBlockType = createSelector(
- canInsertBlockTypeUnmemoized,
- ( state, blockName, rootClientId ) => [
- state.blockListSettings[ rootClientId ],
- state.editor.present.blocks.byClientId[ rootClientId ],
- state.settings.allowedBlockTypes,
- state.settings.templateLock,
- ],
-);
-
-/**
- * Returns information about how recently and frequently a block has been inserted.
- *
- * @param {Object} state Global application state.
- * @param {string} id A string which identifies the insert, e.g. 'core/block/12'
- *
- * @return {?{ time: number, count: number }} An object containing `time` which is when the last
- * insert occurred as a UNIX epoch, and `count` which is
- * the number of inserts that have occurred.
- */
-function getInsertUsage( state, id ) {
- return state.preferences.insertUsage[ id ] || null;
-}
-
-/**
- * Returns whether we can show a block type in the inserter
- *
- * @param {Object} state Global State
- * @param {Object} blockType BlockType
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {boolean} Whether the given block type is allowed to be shown in the inserter.
- */
-const canIncludeBlockTypeInInserter = ( state, blockType, rootClientId ) => {
- if ( ! hasBlockSupport( blockType, 'inserter', true ) ) {
- return false;
- }
-
- return canInsertBlockTypeUnmemoized( state, blockType.name, rootClientId );
-};
-
-/**
- * Returns whether we can show a reusable block in the inserter
- *
- * @param {Object} state Global State
- * @param {Object} reusableBlock Reusable block object
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {boolean} Whether the given block type is allowed to be shown in the inserter.
- */
-const canIncludeReusableBlockInInserter = ( state, reusableBlock, rootClientId ) => {
- if ( ! canInsertBlockTypeUnmemoized( state, 'core/block', rootClientId ) ) {
- return false;
- }
-
- const referencedBlockName = getBlockName( state, reusableBlock.clientId );
- if ( ! referencedBlockName ) {
- return false;
- }
-
- const referencedBlockType = getBlockType( referencedBlockName );
- if ( ! referencedBlockType ) {
- return false;
- }
-
- if ( ! canInsertBlockTypeUnmemoized( state, referencedBlockName, rootClientId ) ) {
- return false;
- }
-
- if ( isAncestorOf( state, reusableBlock.clientId, rootClientId ) ) {
- return false;
- }
-
- return true;
-};
-
-/**
- * Determines the items that appear in the inserter. Includes both static
- * items (e.g. a regular block type) and dynamic items (e.g. a reusable block).
- *
- * Each item object contains what's necessary to display a button in the
- * inserter and handle its selection.
- *
- * The 'utility' property indicates how useful we think an item will be to the
- * user. There are 4 levels of utility:
- *
- * 1. Blocks that are contextually useful (utility = 3)
- * 2. Blocks that have been previously inserted (utility = 2)
- * 3. Blocks that are in the common category (utility = 1)
- * 4. All other blocks (utility = 0)
- *
- * The 'frecency' property is a heuristic (https://en.wikipedia.org/wiki/Frecency)
- * that combines block usage frequenty and recency.
- *
- * Items are returned ordered descendingly by their 'utility' and 'frecency'.
- *
- * @param {Object} state Editor state.
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {Editor.InserterItem[]} Items that appear in inserter.
- *
- * @typedef {Object} Editor.InserterItem
- * @property {string} id Unique identifier for the item.
- * @property {string} name The type of block to create.
- * @property {Object} initialAttributes Attributes to pass to the newly created block.
- * @property {string} title Title of the item, as it appears in the inserter.
- * @property {string} icon Dashicon for the item, as it appears in the inserter.
- * @property {string} category Block category that the item is associated with.
- * @property {string[]} keywords Keywords that can be searched to find this item.
- * @property {boolean} isDisabled Whether or not the user should be prevented from inserting
- * this item.
- * @property {number} utility How useful we think this item is, between 0 and 3.
- * @property {number} frecency Hueristic that combines frequency and recency.
- */
-export const getInserterItems = createSelector(
- ( state, rootClientId = null ) => {
- const calculateUtility = ( category, count, isContextual ) => {
- if ( isContextual ) {
- return INSERTER_UTILITY_HIGH;
- } else if ( count > 0 ) {
- return INSERTER_UTILITY_MEDIUM;
- } else if ( category === 'common' ) {
- return INSERTER_UTILITY_LOW;
- }
- return INSERTER_UTILITY_NONE;
- };
-
- const calculateFrecency = ( time, count ) => {
- if ( ! time ) {
- return count;
- }
-
- // The selector is cached, which means Date.now() is the last time that the
- // relevant state changed. This suits our needs.
- const duration = Date.now() - time;
-
- switch ( true ) {
- case duration < MILLISECONDS_PER_HOUR:
- return count * 4;
- case duration < MILLISECONDS_PER_DAY:
- return count * 2;
- case duration < MILLISECONDS_PER_WEEK:
- return count / 2;
- default:
- return count / 4;
- }
- };
-
- const buildBlockTypeInserterItem = ( blockType ) => {
- const id = blockType.name;
-
- let isDisabled = false;
- if ( ! hasBlockSupport( blockType.name, 'multiple', true ) ) {
- isDisabled = some( getBlocksByClientId( state, getClientIdsWithDescendants( state ) ), { name: blockType.name } );
- }
-
- const isContextual = isArray( blockType.parent );
- const { time, count = 0 } = getInsertUsage( state, id ) || {};
-
- return {
- id,
- name: blockType.name,
- initialAttributes: {},
- title: blockType.title,
- icon: blockType.icon,
- category: blockType.category,
- keywords: blockType.keywords,
- isDisabled,
- utility: calculateUtility( blockType.category, count, isContextual ),
- frecency: calculateFrecency( time, count ),
- hasChildBlocksWithInserterSupport: hasChildBlocksWithInserterSupport( blockType.name ),
- };
- };
-
- const buildReusableBlockInserterItem = ( reusableBlock ) => {
- const id = `core/block/${ reusableBlock.id }`;
-
- const referencedBlockName = getBlockName( state, reusableBlock.clientId );
- const referencedBlockType = getBlockType( referencedBlockName );
-
- const { time, count = 0 } = getInsertUsage( state, id ) || {};
- const utility = calculateUtility( 'reusable', count, false );
- const frecency = calculateFrecency( time, count );
-
- return {
- id,
- name: 'core/block',
- initialAttributes: { ref: reusableBlock.id },
- title: reusableBlock.title,
- icon: referencedBlockType.icon,
- category: 'reusable',
- keywords: [],
- isDisabled: false,
- utility,
- frecency,
- };
- };
-
- const blockTypeInserterItems = getBlockTypes()
- .filter( ( blockType ) => canIncludeBlockTypeInInserter( state, blockType, rootClientId ) )
- .map( buildBlockTypeInserterItem );
-
- const reusableBlockInserterItems = __experimentalGetReusableBlocks( state )
- .filter( ( block ) => canIncludeReusableBlockInInserter( state, block, rootClientId ) )
- .map( buildReusableBlockInserterItem );
-
- return orderBy(
- [ ...blockTypeInserterItems, ...reusableBlockInserterItems ],
- [ 'utility', 'frecency' ],
- [ 'desc', 'desc' ]
- );
- },
- ( state, rootClientId ) => [
- state.blockListSettings[ rootClientId ],
- state.editor.present.blocks.byClientId,
- state.editor.present.blocks.order,
- state.preferences.insertUsage,
- state.settings.allowedBlockTypes,
- state.settings.templateLock,
- state.reusableBlocks.data,
- getBlockTypes(),
- ],
-);
-
-/**
- * Determines whether there are items to show in the inserter.
- * @param {Object} state Editor state.
- * @param {?string} rootClientId Optional root client ID of block list.
- *
- * @return {boolean} Items that appear in inserter.
- */
-export const hasInserterItems = createSelector(
- ( state, rootClientId = null ) => {
- const hasBlockType = some(
- getBlockTypes(),
- ( blockType ) => canIncludeBlockTypeInInserter( state, blockType, rootClientId )
- );
- if ( hasBlockType ) {
- return true;
- }
- const hasReusableBlock = some(
- __experimentalGetReusableBlocks( state ),
- ( block ) => canIncludeReusableBlockInInserter( state, block, rootClientId )
- );
-
- return hasReusableBlock;
- },
- ( state, rootClientId ) => [
- state.blockListSettings[ rootClientId ],
- state.editor.present.blocks.byClientId,
- state.settings.allowedBlockTypes,
- state.settings.templateLock,
- state.reusableBlocks.data,
- getBlockTypes(),
- ],
-);
-
-/**
- * Returns the reusable block with the given ID.
- *
- * @param {Object} state Global application state.
- * @param {number|string} ref The reusable block's ID.
- *
- * @return {Object} The reusable block, or null if none exists.
- */
-export const __experimentalGetReusableBlock = createSelector(
- ( state, ref ) => {
- const block = state.reusableBlocks.data[ ref ];
- if ( ! block ) {
- return null;
- }
-
- const isTemporary = isNaN( parseInt( ref ) );
-
- return {
- ...block,
- id: isTemporary ? ref : +ref,
- isTemporary,
- };
- },
- ( state, ref ) => [
- state.reusableBlocks.data[ ref ],
- ],
-);
-
-/**
- * Returns whether or not the reusable block with the given ID is being saved.
- *
- * @param {Object} state Global application state.
- * @param {string} ref The reusable block's ID.
- *
- * @return {boolean} Whether or not the reusable block is being saved.
- */
-export function __experimentalIsSavingReusableBlock( state, ref ) {
- return state.reusableBlocks.isSaving[ ref ] || false;
-}
-
-/**
- * Returns true if the reusable block with the given ID is being fetched, or
- * false otherwise.
- *
- * @param {Object} state Global application state.
- * @param {string} ref The reusable block's ID.
- *
- * @return {boolean} Whether the reusable block is being fetched.
- */
-export function __experimentalIsFetchingReusableBlock( state, ref ) {
- return !! state.reusableBlocks.isFetching[ ref ];
-}
-
-/**
- * Returns an array of all reusable blocks.
- *
- * @param {Object} state Global application state.
- *
- * @return {Array} An array of all reusable blocks.
- */
-export function __experimentalGetReusableBlocks( state ) {
- return map(
- state.reusableBlocks.data,
- ( value, ref ) => __experimentalGetReusableBlock( state, ref )
- );
-}
+export const __experimentalGetReusableBlocks = createSelector(
+ ( state ) => {
+ return map(
+ state.reusableBlocks.data,
+ ( value, ref ) => __experimentalGetReusableBlock( state, ref )
+ );
+ },
+ ( state ) => [
+ state.reusableBlocks.data,
+ ]
+);
/**
* Returns state object prior to a specified optimist transaction ID, or `null`
@@ -2257,45 +989,6 @@ export function inSomeHistory( state, predicate ) {
) );
}
-/**
- * Returns the Block List settings of a block, if any exist.
- *
- * @param {Object} state Editor state.
- * @param {?string} clientId Block client ID.
- *
- * @return {?Object} Block settings of the block if set.
- */
-export function getBlockListSettings( state, clientId ) {
- return state.blockListSettings[ clientId ];
-}
-
-/**
- * Returns the editor settings.
- *
- * @param {Object} state Editor state.
- *
- * @return {Object} The editor settings object.
- */
-export function getEditorSettings( state ) {
- return state.settings;
-}
-
-/**
- * Returns the token settings.
- *
- * @param {Object} state Editor state.
- * @param {?string} name Token name.
- *
- * @return {Object} Token settings object, or the named token settings object if set.
- */
-export function getTokenSettings( state, name ) {
- if ( ! name ) {
- return state.tokens;
- }
-
- return state.tokens[ name ];
-}
-
/**
* Returns whether the post is locked.
*
@@ -2376,3 +1069,88 @@ export function isPublishSidebarEnabled( state ) {
}
return PREFERENCES_DEFAULTS.isPublishSidebarEnabled;
}
+
+/**
+ * Return the current block list.
+ *
+ * @param {Object} state
+ * @return {Array} Block list.
+ */
+export function getEditorBlocks( state ) {
+ return state.editor.present.blocks.value;
+}
+
+/**
+ * Is the editor ready
+ *
+ * @param {Object} state
+ * @return {boolean} is Ready.
+ */
+export function __unstableIsEditorReady( state ) {
+ return state.isReady;
+}
+
+/*
+ * Backward compatibility
+ */
+
+function getBlockEditorSelector( name ) {
+ return createRegistrySelector( ( select ) => ( state, ...args ) => {
+ return select( 'core/block-editor' )[ name ]( ...args );
+ } );
+}
+
+export const getBlockDependantsCacheBust = getBlockEditorSelector( 'getBlockDependantsCacheBust' );
+export const getBlockName = getBlockEditorSelector( 'getBlockName' );
+export const isBlockValid = getBlockEditorSelector( 'isBlockValid' );
+export const getBlockAttributes = getBlockEditorSelector( 'getBlockAttributes' );
+export const getBlock = getBlockEditorSelector( 'getBlock' );
+export const getBlocks = getBlockEditorSelector( 'getBlocks' );
+export const __unstableGetBlockWithoutInnerBlocks = getBlockEditorSelector( '__unstableGetBlockWithoutInnerBlocks' );
+export const getClientIdsOfDescendants = getBlockEditorSelector( 'getClientIdsOfDescendants' );
+export const getClientIdsWithDescendants = getBlockEditorSelector( 'getClientIdsWithDescendants' );
+export const getGlobalBlockCount = getBlockEditorSelector( 'getGlobalBlockCount' );
+export const getBlocksByClientId = getBlockEditorSelector( 'getBlocksByClientId' );
+export const getBlockCount = getBlockEditorSelector( 'getBlockCount' );
+export const getBlockSelectionStart = getBlockEditorSelector( 'getBlockSelectionStart' );
+export const getBlockSelectionEnd = getBlockEditorSelector( 'getBlockSelectionEnd' );
+export const getSelectedBlockCount = getBlockEditorSelector( 'getSelectedBlockCount' );
+export const hasSelectedBlock = getBlockEditorSelector( 'hasSelectedBlock' );
+export const getSelectedBlockClientId = getBlockEditorSelector( 'getSelectedBlockClientId' );
+export const getSelectedBlock = getBlockEditorSelector( 'getSelectedBlock' );
+export const getBlockRootClientId = getBlockEditorSelector( 'getBlockRootClientId' );
+export const getBlockHierarchyRootClientId = getBlockEditorSelector( 'getBlockHierarchyRootClientId' );
+export const getAdjacentBlockClientId = getBlockEditorSelector( 'getAdjacentBlockClientId' );
+export const getPreviousBlockClientId = getBlockEditorSelector( 'getPreviousBlockClientId' );
+export const getNextBlockClientId = getBlockEditorSelector( 'getNextBlockClientId' );
+export const getSelectedBlocksInitialCaretPosition = getBlockEditorSelector( 'getSelectedBlocksInitialCaretPosition' );
+export const getMultiSelectedBlockClientIds = getBlockEditorSelector( 'getMultiSelectedBlockClientIds' );
+export const getMultiSelectedBlocks = getBlockEditorSelector( 'getMultiSelectedBlocks' );
+export const getFirstMultiSelectedBlockClientId = getBlockEditorSelector( 'getFirstMultiSelectedBlockClientId' );
+export const getLastMultiSelectedBlockClientId = getBlockEditorSelector( 'getLastMultiSelectedBlockClientId' );
+export const isFirstMultiSelectedBlock = getBlockEditorSelector( 'isFirstMultiSelectedBlock' );
+export const isBlockMultiSelected = getBlockEditorSelector( 'isBlockMultiSelected' );
+export const isAncestorMultiSelected = getBlockEditorSelector( 'isAncestorMultiSelected' );
+export const getMultiSelectedBlocksStartClientId = getBlockEditorSelector( 'getMultiSelectedBlocksStartClientId' );
+export const getMultiSelectedBlocksEndClientId = getBlockEditorSelector( 'getMultiSelectedBlocksEndClientId' );
+export const getBlockOrder = getBlockEditorSelector( 'getBlockOrder' );
+export const getBlockIndex = getBlockEditorSelector( 'getBlockIndex' );
+export const isBlockSelected = getBlockEditorSelector( 'isBlockSelected' );
+export const hasSelectedInnerBlock = getBlockEditorSelector( 'hasSelectedInnerBlock' );
+export const isBlockWithinSelection = getBlockEditorSelector( 'isBlockWithinSelection' );
+export const hasMultiSelection = getBlockEditorSelector( 'hasMultiSelection' );
+export const isMultiSelecting = getBlockEditorSelector( 'isMultiSelecting' );
+export const isSelectionEnabled = getBlockEditorSelector( 'isSelectionEnabled' );
+export const getBlockMode = getBlockEditorSelector( 'getBlockMode' );
+export const isTyping = getBlockEditorSelector( 'isTyping' );
+export const isCaretWithinFormattedText = getBlockEditorSelector( 'isCaretWithinFormattedText' );
+export const getBlockInsertionPoint = getBlockEditorSelector( 'getBlockInsertionPoint' );
+export const isBlockInsertionPointVisible = getBlockEditorSelector( 'isBlockInsertionPointVisible' );
+export const isValidTemplate = getBlockEditorSelector( 'isValidTemplate' );
+export const getTemplate = getBlockEditorSelector( 'getTemplate' );
+export const getTemplateLock = getBlockEditorSelector( 'getTemplateLock' );
+export const canInsertBlockType = getBlockEditorSelector( 'canInsertBlockType' );
+export const getInserterItems = getBlockEditorSelector( 'getInserterItems' );
+export const hasInserterItems = getBlockEditorSelector( 'hasInserterItems' );
+export const getEditorSettings = getBlockEditorSelector( 'getEditorSettings' );
+export const getBlockListSettings = getBlockEditorSelector( 'getBlockListSettings' );
diff --git a/packages/editor/src/store/test/actions.js b/packages/editor/src/store/test/actions.js
index 057e86c121aa5..ec46b287c394e 100644
--- a/packages/editor/src/store/test/actions.js
+++ b/packages/editor/src/store/test/actions.js
@@ -2,43 +2,18 @@
* Internal dependencies
*/
import {
- replaceBlocks,
- startTyping,
- stopTyping,
- enterFormattedText,
- exitFormattedText,
__experimentalFetchReusableBlocks as fetchReusableBlocks,
__experimentalSaveReusableBlock as saveReusableBlock,
__experimentalDeleteReusableBlock as deleteReusableBlock,
__experimentalConvertBlockToStatic as convertBlockToStatic,
__experimentalConvertBlockToReusable as convertBlockToReusable,
- toggleSelection,
setupEditor,
resetPost,
- resetBlocks,
- updateBlockAttributes,
- updateBlock,
- selectBlock,
- selectPreviousBlock,
- startMultiSelect,
- stopMultiSelect,
- multiSelect,
- clearSelectedBlock,
- replaceBlock,
- insertBlock,
- insertBlocks,
- showInsertionPoint,
- hideInsertionPoint,
editPost,
savePost,
trashPost,
- mergeBlocks,
redo,
undo,
- removeBlocks,
- removeBlock,
- toggleBlockMode,
- updateBlockListSettings,
} from '../actions';
describe( 'actions', () => {
@@ -63,169 +38,6 @@ describe( 'actions', () => {
} );
} );
} );
- describe( 'resetBlocks', () => {
- it( 'should return the RESET_BLOCKS actions', () => {
- const blocks = [];
- const result = resetBlocks( blocks );
- expect( result ).toEqual( {
- type: 'RESET_BLOCKS',
- blocks,
- } );
- } );
- } );
-
- describe( 'updateBlockAttributes', () => {
- it( 'should return the UPDATE_BLOCK_ATTRIBUTES action', () => {
- const clientId = 'myclientid';
- const attributes = {};
- const result = updateBlockAttributes( clientId, attributes );
- expect( result ).toEqual( {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId,
- attributes,
- } );
- } );
- } );
-
- describe( 'updateBlock', () => {
- it( 'should return the UPDATE_BLOCK action', () => {
- const clientId = 'myclientid';
- const updates = {};
- const result = updateBlock( clientId, updates );
- expect( result ).toEqual( {
- type: 'UPDATE_BLOCK',
- clientId,
- updates,
- } );
- } );
- } );
-
- describe( 'selectBlock', () => {
- it( 'should return the SELECT_BLOCK action', () => {
- const clientId = 'myclientid';
- const result = selectBlock( clientId, -1 );
- expect( result ).toEqual( {
- type: 'SELECT_BLOCK',
- initialPosition: -1,
- clientId,
- } );
- } );
- } );
-
- describe( 'startMultiSelect', () => {
- it( 'should return the START_MULTI_SELECT', () => {
- expect( startMultiSelect() ).toEqual( {
- type: 'START_MULTI_SELECT',
- } );
- } );
- } );
-
- describe( 'stopMultiSelect', () => {
- it( 'should return the Stop_MULTI_SELECT', () => {
- expect( stopMultiSelect() ).toEqual( {
- type: 'STOP_MULTI_SELECT',
- } );
- } );
- } );
- describe( 'multiSelect', () => {
- it( 'should return MULTI_SELECT action', () => {
- const start = 'start';
- const end = 'end';
- expect( multiSelect( start, end ) ).toEqual( {
- type: 'MULTI_SELECT',
- start,
- end,
- } );
- } );
- } );
-
- describe( 'clearSelectedBlock', () => {
- it( 'should return CLEAR_SELECTED_BLOCK action', () => {
- expect( clearSelectedBlock() ).toEqual( {
- type: 'CLEAR_SELECTED_BLOCK',
- } );
- } );
- } );
-
- describe( 'replaceBlock', () => {
- it( 'should return the REPLACE_BLOCKS action', () => {
- const block = {
- clientId: 'ribs',
- };
-
- expect( replaceBlock( [ 'chicken' ], block ) ).toEqual( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ block ],
- time: expect.any( Number ),
- } );
- } );
- } );
-
- describe( 'replaceBlocks', () => {
- it( 'should return the REPLACE_BLOCKS action', () => {
- const blocks = [ {
- clientId: 'ribs',
- } ];
-
- expect( replaceBlocks( [ 'chicken' ], blocks ) ).toEqual( {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks,
- time: expect.any( Number ),
- } );
- } );
- } );
-
- describe( 'insertBlock', () => {
- it( 'should return the INSERT_BLOCKS action', () => {
- const block = {
- clientId: 'ribs',
- };
- const index = 5;
- expect( insertBlock( block, index, 'testclientid' ) ).toEqual( {
- type: 'INSERT_BLOCKS',
- blocks: [ block ],
- index,
- rootClientId: 'testclientid',
- time: expect.any( Number ),
- updateSelection: true,
- } );
- } );
- } );
-
- describe( 'insertBlocks', () => {
- it( 'should return the INSERT_BLOCKS action', () => {
- const blocks = [ {
- clientId: 'ribs',
- } ];
- const index = 3;
- expect( insertBlocks( blocks, index, 'testclientid' ) ).toEqual( {
- type: 'INSERT_BLOCKS',
- blocks,
- index,
- rootClientId: 'testclientid',
- time: expect.any( Number ),
- updateSelection: true,
- } );
- } );
- } );
-
- describe( 'showInsertionPoint', () => {
- it( 'should return the SHOW_INSERTION_POINT action', () => {
- expect( showInsertionPoint() ).toEqual( {
- type: 'SHOW_INSERTION_POINT',
- } );
- } );
- } );
-
- describe( 'hideInsertionPoint', () => {
- it( 'should return the HIDE_INSERTION_POINT action', () => {
- expect( hideInsertionPoint() ).toEqual( {
- type: 'HIDE_INSERTION_POINT',
- } );
- } );
- } );
describe( 'editPost', () => {
it( 'should return EDIT_POST action', () => {
@@ -265,17 +77,6 @@ describe( 'actions', () => {
} );
} );
- describe( 'mergeBlocks', () => {
- it( 'should return MERGE_BLOCKS action', () => {
- const firstBlockClientId = 'blockA';
- const secondBlockClientId = 'blockB';
- expect( mergeBlocks( firstBlockClientId, secondBlockClientId ) ).toEqual( {
- type: 'MERGE_BLOCKS',
- blocks: [ firstBlockClientId, secondBlockClientId ],
- } );
- } );
- } );
-
describe( 'redo', () => {
it( 'should return REDO action', () => {
expect( redo() ).toEqual( {
@@ -292,94 +93,6 @@ describe( 'actions', () => {
} );
} );
- describe( 'removeBlocks', () => {
- it( 'should return REMOVE_BLOCKS action', () => {
- const clientId = 'clientId';
- const clientIds = [ clientId ];
-
- const actions = Array.from( removeBlocks( clientIds ) );
-
- expect( actions ).toEqual( [
- selectPreviousBlock( clientId ),
- {
- type: 'REMOVE_BLOCKS',
- clientIds,
- },
- ] );
- } );
- } );
-
- describe( 'removeBlock', () => {
- it( 'should return REMOVE_BLOCKS action', () => {
- const clientId = 'myclientid';
-
- const actions = Array.from( removeBlock( clientId ) );
-
- expect( actions ).toEqual( [
- selectPreviousBlock( clientId ),
- {
- type: 'REMOVE_BLOCKS',
- clientIds: [ clientId ],
- },
- ] );
- } );
-
- it( 'should return REMOVE_BLOCKS action, opting out of remove previous', () => {
- const clientId = 'myclientid';
-
- const actions = Array.from( removeBlock( clientId, false ) );
-
- expect( actions ).toEqual( [
- {
- type: 'REMOVE_BLOCKS',
- clientIds: [ clientId ],
- },
- ] );
- } );
- } );
-
- describe( 'toggleBlockMode', () => {
- it( 'should return TOGGLE_BLOCK_MODE action', () => {
- const clientId = 'myclientid';
- expect( toggleBlockMode( clientId ) ).toEqual( {
- type: 'TOGGLE_BLOCK_MODE',
- clientId,
- } );
- } );
- } );
-
- describe( 'startTyping', () => {
- it( 'should return the START_TYPING action', () => {
- expect( startTyping() ).toEqual( {
- type: 'START_TYPING',
- } );
- } );
- } );
-
- describe( 'stopTyping', () => {
- it( 'should return the STOP_TYPING action', () => {
- expect( stopTyping() ).toEqual( {
- type: 'STOP_TYPING',
- } );
- } );
- } );
-
- describe( 'enterFormattedText', () => {
- it( 'should return the ENTER_FORMATTED_TEXT action', () => {
- expect( enterFormattedText() ).toEqual( {
- type: 'ENTER_FORMATTED_TEXT',
- } );
- } );
- } );
-
- describe( 'exitFormattedText', () => {
- it( 'should return the EXIT_FORMATTED_TEXT action', () => {
- expect( exitFormattedText() ).toEqual( {
- type: 'EXIT_FORMATTED_TEXT',
- } );
- } );
- } );
-
describe( 'fetchReusableBlocks', () => {
it( 'should return the FETCH_REUSABLE_BLOCKS action', () => {
expect( fetchReusableBlocks() ).toEqual( {
@@ -432,45 +145,4 @@ describe( 'actions', () => {
} );
} );
} );
-
- describe( 'toggleSelection', () => {
- it( 'should return the TOGGLE_SELECTION action with default value for isSelectionEnabled = true', () => {
- expect( toggleSelection() ).toEqual( {
- type: 'TOGGLE_SELECTION',
- isSelectionEnabled: true,
- } );
- } );
-
- it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = true as passed in the argument', () => {
- expect( toggleSelection( true ) ).toEqual( {
- type: 'TOGGLE_SELECTION',
- isSelectionEnabled: true,
- } );
- } );
-
- it( 'should return the TOGGLE_SELECTION action with isSelectionEnabled = false as passed in the argument', () => {
- expect( toggleSelection( false ) ).toEqual( {
- type: 'TOGGLE_SELECTION',
- isSelectionEnabled: false,
- } );
- } );
- } );
-
- describe( 'updateBlockListSettings', () => {
- it( 'should return the UPDATE_BLOCK_LIST_SETTINGS with undefined settings', () => {
- expect( updateBlockListSettings( 'chicken' ) ).toEqual( {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: 'chicken',
- settings: undefined,
- } );
- } );
-
- it( 'should return the UPDATE_BLOCK_LIST_SETTINGS action with the passed settings', () => {
- expect( updateBlockListSettings( 'chicken', { chicken: 'ribs' } ) ).toEqual( {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: 'chicken',
- settings: { chicken: 'ribs' },
- } );
- } );
- } );
} );
diff --git a/packages/editor/src/store/test/effects.js b/packages/editor/src/store/test/effects.js
index a6ce470d86e54..2e9170d2175a0 100644
--- a/packages/editor/src/store/test/effects.js
+++ b/packages/editor/src/store/test/effects.js
@@ -1,8 +1,3 @@
-/**
- * External dependencies
- */
-import { noop } from 'lodash';
-
/**
* WordPress dependencies
*/
@@ -10,27 +5,15 @@ import {
getBlockTypes,
unregisterBlockType,
registerBlockType,
- createBlock,
} from '@wordpress/blocks';
-import { dispatch as dataDispatch, createRegistry } from '@wordpress/data';
+import { dispatch as dataDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
-import actions, {
- updateEditorSettings,
- setupEditorState,
- mergeBlocks,
- replaceBlocks,
- resetBlocks,
- selectBlock,
- setTemplateValidity,
-} from '../actions';
-import effects, { validateBlocksToTemplate } from '../effects';
+import { setupEditorState, resetEditorBlocks } from '../actions';
+import effects from '../effects';
import { SAVE_POST_NOTICE_ID } from '../effects/posts';
-import * as selectors from '../selectors';
-import reducer from '../reducer';
-import applyMiddlewares from '../middlewares';
import '../../';
describe( 'effects', () => {
@@ -46,178 +29,6 @@ describe( 'effects', () => {
const defaultBlockSettings = { save: () => 'Saved', category: 'common', title: 'block title' };
- describe( '.MERGE_BLOCKS', () => {
- const handler = effects.MERGE_BLOCKS;
- const defaultGetBlock = selectors.getBlock;
-
- afterEach( () => {
- getBlockTypes().forEach( ( block ) => {
- unregisterBlockType( block.name );
- } );
- selectors.getBlock = defaultGetBlock;
- } );
-
- it( 'should only focus the blockA if the blockA has no merge function', () => {
- registerBlockType( 'core/test-block', defaultBlockSettings );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block',
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
-
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).toHaveBeenCalledTimes( 1 );
- expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken' ) );
- } );
-
- it( 'should merge the blocks if blocks of the same type', () => {
- registerBlockType( 'core/test-block', {
- merge( attributes, attributesToMerge ) {
- return {
- content: attributes.content + ' ' + attributesToMerge.content,
- };
- },
- save: noop,
- category: 'common',
- title: 'test block',
- } );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken' },
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: { content: 'ribs' },
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).toHaveBeenCalledTimes( 2 );
- expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken', -1 ) );
- expect( dispatch ).toHaveBeenCalledWith( {
- ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken ribs' },
- } ] ),
- time: expect.any( Number ),
- } );
- } );
-
- it( 'should not merge the blocks have different types without transformation', () => {
- registerBlockType( 'core/test-block', {
- merge( attributes, attributesToMerge ) {
- return {
- content: attributes.content + ' ' + attributesToMerge.content,
- };
- },
- save: noop,
- category: 'common',
- title: 'test block',
- } );
- registerBlockType( 'core/test-block-2', defaultBlockSettings );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken' },
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block2',
- attributes: { content: 'ribs' },
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).not.toHaveBeenCalled();
- } );
-
- it( 'should transform and merge the blocks', () => {
- registerBlockType( 'core/test-block', {
- attributes: {
- content: {
- type: 'string',
- },
- },
- merge( attributes, attributesToMerge ) {
- return {
- content: attributes.content + ' ' + attributesToMerge.content,
- };
- },
- save: noop,
- category: 'common',
- title: 'test block',
- } );
- registerBlockType( 'core/test-block-2', {
- attributes: {
- content: {
- type: 'string',
- },
- },
- transforms: {
- to: [ {
- type: 'block',
- blocks: [ 'core/test-block' ],
- transform: ( { content2 } ) => {
- return createBlock( 'core/test-block', {
- content: content2,
- } );
- },
- } ],
- },
- save: noop,
- category: 'common',
- title: 'test block 2',
- } );
- const blockA = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken' },
- };
- const blockB = {
- clientId: 'ribs',
- name: 'core/test-block-2',
- attributes: { content2: 'ribs' },
- };
- selectors.getBlock = ( state, clientId ) => {
- return blockA.clientId === clientId ? blockA : blockB;
- };
- const dispatch = jest.fn();
- const getState = () => ( {} );
- handler( mergeBlocks( blockA.clientId, blockB.clientId ), { dispatch, getState } );
-
- expect( dispatch ).toHaveBeenCalledTimes( 2 );
- // expect( dispatch ).toHaveBeenCalledWith( focusBlock( 'chicken', { offset: -1 } ) );
- expect( dispatch ).toHaveBeenCalledWith( {
- ...replaceBlocks( [ 'chicken', 'ribs' ], [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: { content: 'chicken ribs' },
- } ] ),
- time: expect.any( Number ),
- } );
- } );
- } );
-
describe( '.REQUEST_POST_UPDATE_SUCCESS', () => {
const handler = effects.REQUEST_POST_UPDATE_SUCCESS;
@@ -423,19 +234,11 @@ describe( 'effects', () => {
},
status: 'draft',
};
- const getState = () => ( {
- settings: {
- template: null,
- templateLock: false,
- },
- template: {
- isValid: true,
- },
- } );
- const result = handler( { post, settings: {} }, { getState } );
+ const result = handler( { post, settings: {} } );
expect( result ).toEqual( [
+ resetEditorBlocks( [] ),
setupEditorState( post, [], {} ),
] );
} );
@@ -452,22 +255,11 @@ describe( 'effects', () => {
},
status: 'draft',
};
- const getState = () => ( {
- settings: {
- template: null,
- templateLock: false,
- },
- template: {
- isValid: true,
- },
- } );
- const result = handler( { post }, { getState } );
+ const result = handler( { post } );
expect( result[ 0 ].blocks ).toHaveLength( 1 );
- expect( result ).toEqual( [
- setupEditorState( post, result[ 0 ].blocks, {} ),
- ] );
+ expect( result[ 1 ] ).toEqual( setupEditorState( post, result[ 0 ].blocks, {} ) );
} );
it( 'should return post setup action only if auto-draft', () => {
@@ -481,93 +273,13 @@ describe( 'effects', () => {
},
status: 'auto-draft',
};
- const getState = () => ( {
- settings: {
- template: null,
- templateLock: false,
- },
- template: {
- isValid: true,
- },
- } );
- const result = handler( { post }, { getState } );
+ const result = handler( { post } );
expect( result ).toEqual( [
+ resetEditorBlocks( [] ),
setupEditorState( post, [], { title: 'A History of Pork' } ),
] );
} );
} );
-
- describe( 'validateBlocksToTemplate', () => {
- let store;
- beforeEach( () => {
- store = createRegistry().registerStore( 'test', {
- actions,
- selectors,
- reducer,
- } );
- applyMiddlewares( store );
-
- registerBlockType( 'core/test-block', defaultBlockSettings );
- } );
-
- afterEach( () => {
- getBlockTypes().forEach( ( block ) => {
- unregisterBlockType( block.name );
- } );
- } );
-
- it( 'should return undefined if no template assigned', () => {
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toBe( undefined );
- } );
-
- it( 'should return undefined if invalid but unlocked', () => {
- store.dispatch( updateEditorSettings( {
- template: [
- [ 'core/foo', {} ],
- ],
- } ) );
-
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toBe( undefined );
- } );
-
- it( 'should return undefined if locked and valid', () => {
- store.dispatch( updateEditorSettings( {
- template: [
- [ 'core/test-block' ],
- ],
- templateLock: 'all',
- } ) );
-
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toBe( undefined );
- } );
-
- it( 'should return validity set action if invalid on default state', () => {
- store.dispatch( updateEditorSettings( {
- template: [
- [ 'core/foo' ],
- ],
- templateLock: 'all',
- } ) );
-
- const result = validateBlocksToTemplate( resetBlocks( [
- createBlock( 'core/test-block' ),
- ] ), store );
-
- expect( result ).toEqual( setTemplateValidity( false ) );
- } );
- } );
} );
diff --git a/packages/editor/src/store/test/reducer.js b/packages/editor/src/store/test/reducer.js
index a8172df208a41..f7be763e12170 100644
--- a/packages/editor/src/store/test/reducer.js
+++ b/packages/editor/src/store/test/reducer.js
@@ -1,40 +1,22 @@
/**
* External dependencies
*/
-import { values, noop } from 'lodash';
import deepFreeze from 'deep-freeze';
-/**
- * WordPress dependencies
- */
-import {
- registerBlockType,
- unregisterBlockType,
- createBlock,
-} from '@wordpress/blocks';
-
/**
* Internal dependencies
*/
import {
hasSameKeys,
- isUpdatingSameBlockAttribute,
isUpdatingSamePostProperty,
shouldOverwriteState,
getPostRawValue,
- editor,
initialEdits,
+ editor,
currentPost,
- isTyping,
- isCaretWithinFormattedText,
- blockSelection,
preferences,
saving,
- blocksMode,
- insertionPoint,
reusableBlocks,
- template,
- blockListSettings,
autosave,
postSavingLock,
previewLink,
@@ -58,78 +40,6 @@ describe( 'state', () => {
} );
} );
- describe( 'isUpdatingSameBlockAttribute()', () => {
- it( 'should return false if not updating block attributes', () => {
- const action = {
- type: 'EDIT_POST',
- edits: {},
- };
- const previousAction = {
- type: 'EDIT_POST',
- edits: {},
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return false if not updating the same block', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- attributes: {
- foo: 20,
- },
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return false if not updating the same block attributes', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- bar: 20,
- },
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( false );
- } );
-
- it( 'should return true if updating the same block attributes', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 20,
- },
- };
-
- expect( isUpdatingSameBlockAttribute( action, previousAction ) ).toBe( true );
- } );
- } );
-
describe( 'isUpdatingSamePostProperty()', () => {
it( 'should return false if not editing post', () => {
const action = {
@@ -215,25 +125,6 @@ describe( 'state', () => {
expect( shouldOverwriteState( action, previousAction ) ).toBe( false );
} );
- it( 'should return true if updating same block attribute', () => {
- const action = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 10,
- },
- };
- const previousAction = {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- attributes: {
- foo: 20,
- },
- };
-
- expect( shouldOverwriteState( action, previousAction ) ).toBe( true );
- } );
-
it( 'should return true if updating same post property', () => {
const action = {
type: 'EDIT_POST',
@@ -267,844 +158,148 @@ describe( 'state', () => {
} );
describe( 'editor()', () => {
- beforeAll( () => {
- registerBlockType( 'core/test-block', {
- save: noop,
- edit: noop,
- category: 'common',
- title: 'test block',
- } );
- } );
-
- afterAll( () => {
- unregisterBlockType( 'core/test-block' );
- } );
-
- it( 'should return history (empty edits, blocks) by default', () => {
- const state = editor( undefined, {} );
-
- expect( state.past ).toEqual( [] );
- expect( state.future ).toEqual( [] );
- expect( state.present.edits ).toEqual( {} );
- expect( state.present.blocks.byClientId ).toEqual( {} );
- expect( state.present.blocks.order ).toEqual( {} );
- expect( state.present.blocks.isDirty ).toBe( false );
- } );
-
- it( 'should key by reset blocks clientId', () => {
- const original = editor( undefined, {} );
- const state = editor( original, {
- type: 'RESET_BLOCKS',
- blocks: [ { clientId: 'bananas', innerBlocks: [] } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 1 );
- expect( values( state.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'bananas' );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'bananas' ],
- bananas: [],
- } );
- } );
-
- it( 'should key by reset blocks clientId, including inner blocks', () => {
- const original = editor( undefined, {} );
- const state = editor( original, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'bananas',
- innerBlocks: [ { clientId: 'apples', innerBlocks: [] } ],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 2 );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'bananas' ],
- apples: [],
- bananas: [ 'apples' ],
- } );
- } );
-
- it( 'should insert block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'ribs',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 2 );
- expect( values( state.present.blocks.byClientId )[ 1 ].clientId ).toBe( 'ribs' );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'chicken', 'ribs' ],
- chicken: [],
- ribs: [],
- } );
- } );
-
- it( 'should replace the block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ {
- clientId: 'wings',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 1 );
- expect( values( state.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
- expect( values( state.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'wings' );
- expect( state.present.blocks.order ).toEqual( {
- '': [ 'wings' ],
- wings: [],
- } );
- } );
-
- it( 'should replace the nested block', () => {
- const nestedBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
- const replacementBlock = createBlock( 'core/test-block' );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
-
- const state = editor( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ nestedBlock.clientId ],
- blocks: [ replacementBlock ],
- } );
+ describe( 'blocks()', () => {
+ it( 'should set its value by RESET_EDITOR_BLOCKS', () => {
+ const blocks = [ {
+ clientId: 'block3',
+ innerBlocks: [
+ { clientId: 'block31', innerBlocks: [] },
+ { clientId: 'block32', innerBlocks: [] },
+ ],
+ } ];
+ const state = editor( undefined, {
+ type: 'RESET_EDITOR_BLOCKS',
+ blocks,
+ } );
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ replacementBlock.clientId ],
- [ replacementBlock.clientId ]: [],
+ expect( state.present.blocks.value ).toBe( blocks );
} );
} );
- it( 'should replace the block even if the new block clientId is the same', () => {
- const originalState = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const replacedState = editor( originalState, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ {
- clientId: 'chicken',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( replacedState.present.blocks.byClientId ) ).toHaveLength( 1 );
- expect( values( originalState.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/test-block' );
- expect( values( replacedState.present.blocks.byClientId )[ 0 ].name ).toBe( 'core/freeform' );
- expect( values( replacedState.present.blocks.byClientId )[ 0 ].clientId ).toBe( 'chicken' );
- expect( replacedState.present.blocks.order ).toEqual( {
- '': [ 'chicken' ],
- chicken: [],
- } );
-
- const nestedBlock = {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- };
- const wrapperBlock = createBlock( 'core/test-block', {}, [ nestedBlock ] );
- const replacementNestedBlock = {
- clientId: 'chicken',
- name: 'core/freeform',
- attributes: {},
- innerBlocks: [],
- };
-
- const originalNestedState = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
+ describe( 'edits()', () => {
+ it( 'should save newly edited properties', () => {
+ const original = editor( undefined, {
+ type: 'EDIT_POST',
+ edits: {
+ status: 'draft',
+ title: 'post title',
+ },
+ } );
- const replacedNestedState = editor( originalNestedState, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ nestedBlock.clientId ],
- blocks: [ replacementNestedBlock ],
- } );
+ const state = editor( original, {
+ type: 'EDIT_POST',
+ edits: {
+ tags: [ 1 ],
+ },
+ } );
- expect( replacedNestedState.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ replacementNestedBlock.clientId ],
- [ replacementNestedBlock.clientId ]: [],
+ expect( state.present.edits ).toEqual( {
+ status: 'draft',
+ title: 'post title',
+ tags: [ 1 ],
+ } );
} );
- expect( originalNestedState.present.blocks.byClientId.chicken.name ).toBe( 'core/test-block' );
- expect( replacedNestedState.present.blocks.byClientId.chicken.name ).toBe( 'core/freeform' );
- } );
+ it( 'should return same reference if no changed properties', () => {
+ const original = editor( undefined, {
+ type: 'EDIT_POST',
+ edits: {
+ status: 'draft',
+ title: 'post title',
+ },
+ } );
- it( 'should update the block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- isValid: false,
- innerBlocks: [],
- } ],
- } );
- const state = editor( deepFreeze( original ), {
- type: 'UPDATE_BLOCK',
- clientId: 'chicken',
- updates: {
- attributes: { content: 'ribs' },
- isValid: true,
- },
- } );
+ const state = editor( original, {
+ type: 'EDIT_POST',
+ edits: {
+ status: 'draft',
+ },
+ } );
- expect( state.present.blocks.byClientId.chicken ).toEqual( {
- clientId: 'chicken',
- name: 'core/test-block',
- isValid: true,
+ expect( state.present.edits ).toBe( original.present.edits );
} );
- expect( state.present.blocks.attributes.chicken ).toEqual( {
- content: 'ribs',
- } );
- } );
+ it( 'should save modified properties', () => {
+ const original = editor( undefined, {
+ type: 'EDIT_POST',
+ edits: {
+ status: 'draft',
+ title: 'post title',
+ tags: [ 1 ],
+ },
+ } );
- it( 'should update the reusable block reference if the temporary id is swapped', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/block',
- attributes: {
- ref: 'random-clientId',
+ const state = editor( original, {
+ type: 'EDIT_POST',
+ edits: {
+ title: 'modified title',
+ tags: [ 2 ],
},
- isValid: false,
- innerBlocks: [],
- } ],
- } );
+ } );
- const state = editor( deepFreeze( original ), {
- type: 'SAVE_REUSABLE_BLOCK_SUCCESS',
- id: 'random-clientId',
- updatedId: 3,
+ expect( state.present.edits ).toEqual( {
+ status: 'draft',
+ title: 'modified title',
+ tags: [ 2 ],
+ } );
} );
- expect( state.present.blocks.byClientId.chicken ).toEqual( {
- clientId: 'chicken',
- name: 'core/block',
- isValid: false,
- } );
+ it( 'should merge object values', () => {
+ const original = editor( undefined, {
+ type: 'EDIT_POST',
+ edits: {
+ meta: {
+ a: 1,
+ },
+ },
+ } );
- expect( state.present.blocks.attributes.chicken ).toEqual( {
- ref: 3,
- } );
- } );
+ const state = editor( original, {
+ type: 'EDIT_POST',
+ edits: {
+ meta: {
+ b: 2,
+ },
+ },
+ } );
- it( 'should move the block up', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'ribs' ],
+ expect( state.present.edits ).toEqual( {
+ meta: {
+ a: 1,
+ b: 2,
+ },
+ } );
} );
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
- } );
+ it( 'return state by reference on unchanging update', () => {
+ const original = editor( undefined, {} );
- it( 'should move the nested block up', () => {
- const movedBlock = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlock ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ movedBlock.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
+ const state = editor( original, {
+ type: 'UPDATE_POST',
+ edits: {},
+ } );
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ movedBlock.clientId, siblingBlock.clientId ],
- [ movedBlock.clientId ]: [],
- [ siblingBlock.clientId ]: [],
+ expect( state.present.edits ).toBe( original.present.edits );
} );
- } );
- it( 'should move multiple blocks up', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'ribs', 'veggies' ],
- } );
+ it( 'unset reset post values which match by canonical value', () => {
+ const original = editor( undefined, {
+ type: 'EDIT_POST',
+ edits: {
+ title: 'modified title',
+ },
+ } );
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'veggies', 'chicken' ] );
- } );
+ const state = editor( original, {
+ type: 'RESET_POST',
+ post: {
+ title: {
+ raw: 'modified title',
+ },
+ },
+ } );
- it( 'should move multiple nested blocks up', () => {
- const movedBlockA = createBlock( 'core/test-block' );
- const movedBlockB = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ siblingBlock, movedBlockA, movedBlockB ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ movedBlockA.clientId, movedBlockB.clientId, siblingBlock.clientId ],
- [ movedBlockA.clientId ]: [],
- [ movedBlockB.clientId ]: [],
- [ siblingBlock.clientId ]: [],
- } );
- } );
-
- it( 'should not move the first block up', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'chicken' ],
- } );
-
- expect( state.present.blocks.order ).toBe( original.present.blocks.order );
- } );
-
- it( 'should move the block down', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ 'chicken' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken' ] );
- } );
-
- it( 'should move the nested block down', () => {
- const movedBlock = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlock, siblingBlock ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ movedBlock.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlock.clientId ],
- [ movedBlock.clientId ]: [],
- [ siblingBlock.clientId ]: [],
- } );
- } );
-
- it( 'should move multiple blocks down', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ 'chicken', 'ribs' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'veggies', 'chicken', 'ribs' ] );
- } );
-
- it( 'should move multiple nested blocks down', () => {
- const movedBlockA = createBlock( 'core/test-block' );
- const movedBlockB = createBlock( 'core/test-block' );
- const siblingBlock = createBlock( 'core/test-block' );
- const wrapperBlock = createBlock( 'core/test-block', {}, [ movedBlockA, movedBlockB, siblingBlock ] );
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ wrapperBlock ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ movedBlockA.clientId, movedBlockB.clientId ],
- rootClientId: wrapperBlock.clientId,
- } );
-
- expect( state.present.blocks.order ).toEqual( {
- '': [ wrapperBlock.clientId ],
- [ wrapperBlock.clientId ]: [ siblingBlock.clientId, movedBlockA.clientId, movedBlockB.clientId ],
- [ movedBlockA.clientId ]: [],
- [ movedBlockB.clientId ]: [],
- [ siblingBlock.clientId ]: [],
- } );
- } );
-
- it( 'should not move the last block down', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCKS_DOWN',
- clientIds: [ 'ribs' ],
- } );
-
- expect( state.present.blocks.order ).toBe( original.present.blocks.order );
- } );
-
- it( 'should remove the block', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'chicken' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs' ] );
- expect( state.present.blocks.order ).not.toHaveProperty( 'chicken' );
- expect( state.present.blocks.byClientId ).toEqual( {
- ribs: {
- clientId: 'ribs',
- name: 'core/test-block',
- },
- } );
- expect( state.present.blocks.attributes ).toEqual( {
- ribs: {},
- } );
- } );
-
- it( 'should remove multiple blocks', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'chicken', 'veggies' ],
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs' ] );
- expect( state.present.blocks.order ).not.toHaveProperty( 'chicken' );
- expect( state.present.blocks.order ).not.toHaveProperty( 'veggies' );
- expect( state.present.blocks.byClientId ).toEqual( {
- ribs: {
- clientId: 'ribs',
- name: 'core/test-block',
- },
- } );
- expect( state.present.blocks.attributes ).toEqual( {
- ribs: {},
- } );
- } );
-
- it( 'should cascade remove to include inner blocks', () => {
- const block = createBlock( 'core/test-block', {}, [
- createBlock( 'core/test-block', {}, [
- createBlock( 'core/test-block' ),
- ] ),
- ] );
-
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ block ],
- } );
-
- const state = editor( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ block.clientId ],
- } );
-
- expect( state.present.blocks.byClientId ).toEqual( {} );
- expect( state.present.blocks.order ).toEqual( {
- '': [],
- } );
- } );
-
- it( 'should insert at the specified index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'loquat',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
-
- const state = editor( original, {
- type: 'INSERT_BLOCKS',
- index: 1,
- blocks: [ {
- clientId: 'persimmon',
- name: 'core/freeform',
- innerBlocks: [],
- } ],
- } );
-
- expect( Object.keys( state.present.blocks.byClientId ) ).toHaveLength( 3 );
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'kumquat', 'persimmon', 'loquat' ] );
- } );
-
- it( 'should move block to lower index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCK_TO_POSITION',
- clientId: 'ribs',
- index: 0,
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'ribs', 'chicken', 'veggies' ] );
- } );
-
- it( 'should move block to higher index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCK_TO_POSITION',
- clientId: 'ribs',
- index: 2,
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'chicken', 'veggies', 'ribs' ] );
- } );
-
- it( 'should not move block if passed same index', () => {
- const original = editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'chicken',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'ribs',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'veggies',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- const state = editor( original, {
- type: 'MOVE_BLOCK_TO_POSITION',
- clientId: 'ribs',
- index: 1,
- } );
-
- expect( state.present.blocks.order[ '' ] ).toEqual( [ 'chicken', 'ribs', 'veggies' ] );
- } );
-
- describe( 'edits()', () => {
- it( 'should save newly edited properties', () => {
- const original = editor( undefined, {
- type: 'EDIT_POST',
- edits: {
- status: 'draft',
- title: 'post title',
- },
- } );
-
- const state = editor( original, {
- type: 'EDIT_POST',
- edits: {
- tags: [ 1 ],
- },
- } );
-
- expect( state.present.edits ).toEqual( {
- status: 'draft',
- title: 'post title',
- tags: [ 1 ],
- } );
- } );
-
- it( 'should return same reference if no changed properties', () => {
- const original = editor( undefined, {
- type: 'EDIT_POST',
- edits: {
- status: 'draft',
- title: 'post title',
- },
- } );
-
- const state = editor( original, {
- type: 'EDIT_POST',
- edits: {
- status: 'draft',
- },
- } );
-
- expect( state.present.edits ).toBe( original.present.edits );
- } );
-
- it( 'should save modified properties', () => {
- const original = editor( undefined, {
- type: 'EDIT_POST',
- edits: {
- status: 'draft',
- title: 'post title',
- tags: [ 1 ],
- },
- } );
-
- const state = editor( original, {
- type: 'EDIT_POST',
- edits: {
- title: 'modified title',
- tags: [ 2 ],
- },
- } );
-
- expect( state.present.edits ).toEqual( {
- status: 'draft',
- title: 'modified title',
- tags: [ 2 ],
- } );
- } );
-
- it( 'should merge object values', () => {
- const original = editor( undefined, {
- type: 'EDIT_POST',
- edits: {
- meta: {
- a: 1,
- },
- },
- } );
-
- const state = editor( original, {
- type: 'EDIT_POST',
- edits: {
- meta: {
- b: 2,
- },
- },
- } );
-
- expect( state.present.edits ).toEqual( {
- meta: {
- a: 1,
- b: 2,
- },
- } );
- } );
-
- it( 'return state by reference on unchanging update', () => {
- const original = editor( undefined, {} );
-
- const state = editor( original, {
- type: 'UPDATE_POST',
- edits: {},
- } );
-
- expect( state.present.edits ).toBe( original.present.edits );
- } );
-
- it( 'unset reset post values which match by canonical value', () => {
- const original = editor( undefined, {
- type: 'EDIT_POST',
- edits: {
- title: 'modified title',
- },
- } );
-
- const state = editor( original, {
- type: 'RESET_POST',
- post: {
- title: {
- raw: 'modified title',
- },
- },
- } );
-
- expect( state.present.edits ).toEqual( {} );
+ expect( state.present.edits ).toEqual( {} );
} );
it( 'unset reset post values by deep match', () => {
@@ -1119,812 +314,173 @@ describe( 'state', () => {
},
} );
- const state = editor( original, {
- type: 'UPDATE_POST',
- edits: {
- title: 'modified title',
- meta: {
- a: 1,
- b: 2,
- },
- },
- } );
-
- expect( state.present.edits ).toEqual( {} );
- } );
-
- it( 'should omit content when resetting', () => {
- // Use case: When editing in Text mode, we defer to content on
- // the property, but we reset blocks by parse when switching
- // back to Visual mode.
- const original = deepFreeze( editor( undefined, {} ) );
- let state = editor( original, {
- type: 'EDIT_POST',
- edits: {
- content: 'bananas',
- },
- } );
-
- expect( state.present.edits ).toHaveProperty( 'content' );
-
- state = editor( original, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- }, {
- clientId: 'loquat',
- name: 'core/test-block',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
-
- expect( state.present.edits ).not.toHaveProperty( 'content' );
- } );
- } );
-
- describe( 'blocks', () => {
- it( 'should not reset any blocks that are not in the post', () => {
- const actions = [
- {
- type: 'RESET_BLOCKS',
- blocks: [
- {
- clientId: 'block1',
- innerBlocks: [
- { clientId: 'block11', innerBlocks: [] },
- { clientId: 'block12', innerBlocks: [] },
- ],
- },
- ],
- },
- {
- type: 'RECEIVE_BLOCKS',
- blocks: [
- {
- clientId: 'block2',
- innerBlocks: [
- { clientId: 'block21', innerBlocks: [] },
- { clientId: 'block22', innerBlocks: [] },
- ],
- },
- ],
- },
- ];
- const original = deepFreeze( actions.reduce( editor, undefined ) );
-
- const state = editor( original, {
- type: 'RESET_BLOCKS',
- blocks: [
- {
- clientId: 'block3',
- innerBlocks: [
- { clientId: 'block31', innerBlocks: [] },
- { clientId: 'block32', innerBlocks: [] },
- ],
- },
- ],
- } );
-
- expect( state.present.blocks.byClientId ).toEqual( {
- block2: { clientId: 'block2' },
- block21: { clientId: 'block21' },
- block22: { clientId: 'block22' },
- block3: { clientId: 'block3' },
- block31: { clientId: 'block31' },
- block32: { clientId: 'block32' },
- } );
- } );
-
- describe( 'byClientId', () => {
- it( 'should ignore updates to non-existent block', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.byClientId ).toBe( original.present.blocks.byClientId );
- } );
-
- it( 'should return with same reference if no changes in updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.byClientId ).toBe( state.present.blocks.byClientId );
- } );
- } );
-
- describe( 'attributes', () => {
- it( 'should return with attribute block updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {},
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.attributes.kumquat.updated ).toBe( true );
- } );
-
- it( 'should accumulate attribute block updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- moreUpdated: true,
- },
- } );
-
- expect( state.present.blocks.attributes.kumquat ).toEqual( {
- updated: true,
- moreUpdated: true,
- } );
- } );
-
- it( 'should ignore updates to non-existent block', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.attributes ).toBe( original.present.blocks.attributes );
- } );
-
- it( 'should return with same reference if no changes in updates', () => {
- const original = deepFreeze( editor( undefined, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- innerBlocks: [],
- } ],
- } ) );
- const state = editor( original, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- updated: true,
- },
- } );
-
- expect( state.present.blocks.attributes ).toBe( state.present.blocks.attributes );
- } );
- } );
- } );
-
- describe( 'withHistory', () => {
- it( 'should overwrite present history if updating same attributes', () => {
- let state;
-
- state = editor( state, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
-
- expect( state.past ).toHaveLength( 1 );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- test: 1,
- },
- } );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- test: 2,
- },
- } );
-
- expect( state.past ).toHaveLength( 2 );
- } );
-
- it( 'should not overwrite present history if updating different attributes', () => {
- let state;
-
- state = editor( state, {
- type: 'RESET_BLOCKS',
- blocks: [ {
- clientId: 'kumquat',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
-
- expect( state.past ).toHaveLength( 1 );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- test: 1,
- },
- } );
-
- state = editor( state, {
- type: 'UPDATE_BLOCK_ATTRIBUTES',
- clientId: 'kumquat',
- attributes: {
- other: 1,
- },
- } );
-
- expect( state.past ).toHaveLength( 3 );
- } );
- } );
- } );
-
- describe( 'initialEdits', () => {
- it( 'should default to initial edits', () => {
- const state = initialEdits( undefined, {} );
-
- expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
- } );
-
- it( 'should return initial edits on post reset', () => {
- const state = initialEdits( undefined, {
- type: 'RESET_POST',
- } );
-
- expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
- } );
-
- it( 'should return referentially equal state if setup includes no edits', () => {
- const original = initialEdits( undefined, {} );
- const state = initialEdits( deepFreeze( original ), {
- type: 'SETUP_EDITOR',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should return referentially equal state if reset while having made no edits', () => {
- const original = initialEdits( undefined, {} );
- const state = initialEdits( deepFreeze( original ), {
- type: 'RESET_POST',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should return setup edits', () => {
- const original = initialEdits( undefined, {} );
- const state = initialEdits( deepFreeze( original ), {
- type: 'SETUP_EDITOR',
- edits: {
- title: '',
- content: '',
- },
- } );
-
- expect( state ).toEqual( {
- title: '',
- content: '',
- } );
- } );
-
- it( 'should unset content on editor setup', () => {
- const original = initialEdits( undefined, {
- type: 'SETUP_EDITOR',
- edits: {
- title: '',
- content: '',
- },
- } );
- const state = initialEdits( deepFreeze( original ), {
- type: 'SETUP_EDITOR_STATE',
- } );
-
- expect( state ).toEqual( { title: '' } );
- } );
-
- it( 'should unset values on post update', () => {
- const original = initialEdits( undefined, {
- type: 'SETUP_EDITOR',
- edits: {
- title: '',
- },
- } );
- const state = initialEdits( deepFreeze( original ), {
- type: 'UPDATE_POST',
- edits: {
- title: '',
- },
- } );
-
- expect( state ).toEqual( {} );
- } );
- } );
-
- describe( 'currentPost()', () => {
- it( 'should reset a post object', () => {
- const original = deepFreeze( { title: 'unmodified' } );
-
- const state = currentPost( original, {
- type: 'RESET_POST',
- post: {
- title: 'new post',
- },
- } );
-
- expect( state ).toEqual( {
- title: 'new post',
- } );
- } );
-
- it( 'should update the post object with UPDATE_POST', () => {
- const original = deepFreeze( { title: 'unmodified', status: 'publish' } );
-
- const state = currentPost( original, {
- type: 'UPDATE_POST',
- edits: {
- title: 'updated post object from server',
- },
- } );
-
- expect( state ).toEqual( {
- title: 'updated post object from server',
- status: 'publish',
- } );
- } );
- } );
-
- describe( 'insertionPoint', () => {
- it( 'should default to null', () => {
- const state = insertionPoint( undefined, {} );
-
- expect( state ).toBe( null );
- } );
-
- it( 'should set insertion point', () => {
- const state = insertionPoint( null, {
- type: 'SHOW_INSERTION_POINT',
- rootClientId: 'clientId1',
- index: 0,
- } );
-
- expect( state ).toEqual( {
- rootClientId: 'clientId1',
- index: 0,
- } );
- } );
-
- it( 'should clear the insertion point', () => {
- const original = deepFreeze( {
- rootClientId: 'clientId1',
- index: 0,
- } );
- const state = insertionPoint( original, {
- type: 'HIDE_INSERTION_POINT',
- } );
-
- expect( state ).toBe( null );
- } );
- } );
-
- describe( 'isTyping()', () => {
- it( 'should set the typing flag to true', () => {
- const state = isTyping( false, {
- type: 'START_TYPING',
- } );
-
- expect( state ).toBe( true );
- } );
-
- it( 'should set the typing flag to false', () => {
- const state = isTyping( false, {
- type: 'STOP_TYPING',
- } );
-
- expect( state ).toBe( false );
- } );
- } );
-
- describe( 'isCaretWithinFormattedText()', () => {
- it( 'should set the flag to true', () => {
- const state = isCaretWithinFormattedText( false, {
- type: 'ENTER_FORMATTED_TEXT',
- } );
-
- expect( state ).toBe( true );
- } );
-
- it( 'should set the flag to false', () => {
- const state = isCaretWithinFormattedText( true, {
- type: 'EXIT_FORMATTED_TEXT',
- } );
-
- expect( state ).toBe( false );
- } );
- } );
-
- describe( 'blockSelection()', () => {
- it( 'should return with block clientId as selected', () => {
- const state = blockSelection( undefined, {
- type: 'SELECT_BLOCK',
- clientId: 'kumquat',
- initialPosition: -1,
- } );
-
- expect( state ).toEqual( {
- start: 'kumquat',
- end: 'kumquat',
- initialPosition: -1,
- isMultiSelecting: false,
- isEnabled: true,
- } );
- } );
-
- it( 'should set multi selection', () => {
- const original = deepFreeze( { isMultiSelecting: false } );
- const state = blockSelection( original, {
- type: 'MULTI_SELECT',
- start: 'ribs',
- end: 'chicken',
- } );
-
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should set continuous multi selection', () => {
- const original = deepFreeze( { isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'MULTI_SELECT',
- start: 'ribs',
- end: 'chicken',
- } );
-
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: true,
- } );
- } );
-
- it( 'should start multi selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: false } );
- const state = blockSelection( original, {
- type: 'START_MULTI_SELECT',
- } );
-
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'ribs',
- initialPosition: null,
- isMultiSelecting: true,
- } );
- } );
-
- it( 'should return same reference if already multi-selecting', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'START_MULTI_SELECT',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should end multi selection with selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'STOP_MULTI_SELECT',
- } );
-
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should return same reference if already ended multi-selecting', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken', isMultiSelecting: false } );
- const state = blockSelection( original, {
- type: 'STOP_MULTI_SELECT',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should end multi selection without selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs', isMultiSelecting: true } );
- const state = blockSelection( original, {
- type: 'STOP_MULTI_SELECT',
- } );
-
- expect( state ).toEqual( {
- start: 'ribs',
- end: 'ribs',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should not update the state if the block is already selected', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
+ const state = editor( original, {
+ type: 'UPDATE_POST',
+ edits: {
+ title: 'modified title',
+ meta: {
+ a: 1,
+ b: 2,
+ },
+ },
+ } );
- const state1 = blockSelection( original, {
- type: 'SELECT_BLOCK',
- clientId: 'ribs',
+ expect( state.present.edits ).toEqual( {} );
} );
- expect( state1 ).toBe( original );
- } );
+ it( 'should omit content when resetting', () => {
+ // Use case: When editing in Text mode, we defer to content on
+ // the property, but we reset blocks by parse when switching
+ // back to Visual mode.
+ const original = deepFreeze( editor( undefined, {} ) );
+ let state = editor( original, {
+ type: 'EDIT_POST',
+ edits: {
+ content: 'bananas',
+ },
+ } );
- it( 'should unset multi selection', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
+ expect( state.present.edits ).toHaveProperty( 'content' );
- const state1 = blockSelection( original, {
- type: 'CLEAR_SELECTED_BLOCK',
- } );
+ state = editor( original, {
+ type: 'RESET_EDITOR_BLOCKS',
+ blocks: [ {
+ clientId: 'kumquat',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ }, {
+ clientId: 'loquat',
+ name: 'core/test-block',
+ attributes: {},
+ innerBlocks: [],
+ } ],
+ } );
- expect( state1 ).toEqual( {
- start: null,
- end: null,
- initialPosition: null,
- isMultiSelecting: false,
+ expect( state.present.edits ).not.toHaveProperty( 'content' );
} );
} );
+ } );
- it( 'should return same reference if clearing selection but no selection', () => {
- const original = deepFreeze( { start: null, end: null, isMultiSelecting: false } );
-
- const state1 = blockSelection( original, {
- type: 'CLEAR_SELECTED_BLOCK',
- } );
+ describe( 'initialEdits', () => {
+ it( 'should default to initial edits', () => {
+ const state = initialEdits( undefined, {} );
- expect( state1 ).toBe( original );
+ expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
} );
- it( 'should select inserted block', () => {
- const original = deepFreeze( { start: 'ribs', end: 'chicken' } );
-
- const state3 = blockSelection( original, {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'ribs',
- name: 'core/freeform',
- } ],
- updateSelection: true,
+ it( 'should return initial edits on post reset', () => {
+ const state = initialEdits( undefined, {
+ type: 'RESET_POST',
} );
- expect( state3 ).toEqual( {
- start: 'ribs',
- end: 'ribs',
- initialPosition: null,
- isMultiSelecting: false,
- } );
+ expect( state ).toBe( INITIAL_EDITS_DEFAULTS );
} );
- it( 'should not select inserted block if updateSelection flag is false', () => {
- const original = deepFreeze( { start: 'a', end: 'b' } );
-
- const state3 = blockSelection( original, {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'ribs',
- name: 'core/freeform',
- } ],
- updateSelection: false,
+ it( 'should return referentially equal state if setup includes no edits', () => {
+ const original = initialEdits( undefined, {} );
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'SETUP_EDITOR',
} );
- expect( state3 ).toEqual( {
- start: 'a',
- end: 'b',
- } );
+ expect( state ).toBe( original );
} );
- it( 'should not update the state if the block moved is already selected', () => {
- const original = deepFreeze( { start: 'ribs', end: 'ribs' } );
- const state = blockSelection( original, {
- type: 'MOVE_BLOCKS_UP',
- clientIds: [ 'ribs' ],
+ it( 'should return referentially equal state if reset while having made no edits', () => {
+ const original = initialEdits( undefined, {} );
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'RESET_POST',
} );
expect( state ).toBe( original );
} );
- it( 'should replace the selected block', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [ {
- clientId: 'wings',
- name: 'core/freeform',
- } ],
+ it( 'should return setup edits', () => {
+ const original = initialEdits( undefined, {} );
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'SETUP_EDITOR',
+ edits: {
+ title: '',
+ content: '',
+ },
} );
expect( state ).toEqual( {
- start: 'wings',
- end: 'wings',
- initialPosition: null,
- isMultiSelecting: false,
+ title: '',
+ content: '',
} );
} );
- it( 'should not replace the selected block if we keep it at the end when replacing blocks', () => {
- const original = deepFreeze( { start: 'wings', end: 'wings' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'wings' ],
- blocks: [
- {
- clientId: 'chicken',
- name: 'core/freeform',
- },
- {
- clientId: 'wings',
- name: 'core/freeform',
- } ],
+ it( 'should unset content on editor setup', () => {
+ const original = initialEdits( undefined, {
+ type: 'SETUP_EDITOR',
+ edits: {
+ title: '',
+ content: '',
+ },
+ } );
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'SETUP_EDITOR_STATE',
} );
- expect( state ).toBe( original );
+ expect( state ).toEqual( { title: '' } );
} );
- it( 'should replace the selected block if we keep it not at the end when replacing blocks', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [
- {
- clientId: 'chicken',
- name: 'core/freeform',
- },
- {
- clientId: 'wings',
- name: 'core/freeform',
- } ],
+ it( 'should unset values on post update', () => {
+ const original = initialEdits( undefined, {
+ type: 'SETUP_EDITOR',
+ edits: {
+ title: '',
+ },
} );
-
- expect( state ).toEqual( {
- start: 'wings',
- end: 'wings',
- initialPosition: null,
- isMultiSelecting: false,
+ const state = initialEdits( deepFreeze( original ), {
+ type: 'UPDATE_POST',
+ edits: {
+ title: '',
+ },
} );
+
+ expect( state ).toEqual( {} );
} );
+ } );
+
+ describe( 'currentPost()', () => {
+ it( 'should reset a post object', () => {
+ const original = deepFreeze( { title: 'unmodified' } );
- it( 'should reset if replacing with empty set', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'chicken' ],
- blocks: [],
+ const state = currentPost( original, {
+ type: 'RESET_POST',
+ post: {
+ title: 'new post',
+ },
} );
expect( state ).toEqual( {
- start: null,
- end: null,
- initialPosition: null,
- isMultiSelecting: false,
+ title: 'new post',
} );
} );
- it( 'should keep the selected block', () => {
- const original = deepFreeze( { start: 'chicken', end: 'chicken' } );
- const state = blockSelection( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'ribs' ],
- blocks: [ {
- clientId: 'wings',
- name: 'core/freeform',
- } ],
- } );
-
- expect( state ).toBe( original );
- } );
+ it( 'should update the post object with UPDATE_POST', () => {
+ const original = deepFreeze( { title: 'unmodified', status: 'publish' } );
- it( 'should remove the selection if we are removing the selected block', () => {
- const original = deepFreeze( {
- start: 'chicken',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- const state = blockSelection( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'chicken' ],
+ const state = currentPost( original, {
+ type: 'UPDATE_POST',
+ edits: {
+ title: 'updated post object from server',
+ },
} );
expect( state ).toEqual( {
- start: null,
- end: null,
- initialPosition: null,
- isMultiSelecting: false,
- } );
- } );
-
- it( 'should keep the selection if we are not removing the selected block', () => {
- const original = deepFreeze( {
- start: 'chicken',
- end: 'chicken',
- initialPosition: null,
- isMultiSelecting: false,
- } );
- const state = blockSelection( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'ribs' ],
+ title: 'updated post object from server',
+ status: 'publish',
} );
-
- expect( state ).toBe( original );
} );
} );
describe( 'preferences()', () => {
it( 'should apply all defaults', () => {
const state = preferences( undefined, {} );
-
expect( state ).toEqual( {
- insertUsage: {},
isPublishSidebarEnabled: true,
} );
} );
@@ -1946,84 +502,6 @@ describe( 'state', () => {
expect( state.isPublishSidebarEnabled ).toBe( true );
} );
-
- it( 'should record recently used blocks', () => {
- const state = preferences( deepFreeze( { insertUsage: {} } ), {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'bacon',
- name: 'core-embed/twitter',
- } ],
- time: 123456,
- } );
-
- expect( state ).toEqual( {
- insertUsage: {
- 'core-embed/twitter': {
- time: 123456,
- count: 1,
- insert: { name: 'core-embed/twitter' },
- },
- },
- } );
-
- const twoRecentBlocks = preferences( deepFreeze( {
- insertUsage: {
- 'core-embed/twitter': {
- time: 123456,
- count: 1,
- insert: { name: 'core-embed/twitter' },
- },
- },
- } ), {
- type: 'INSERT_BLOCKS',
- blocks: [ {
- clientId: 'eggs',
- name: 'core-embed/twitter',
- }, {
- clientId: 'bacon',
- name: 'core/block',
- attributes: { ref: 123 },
- } ],
- time: 123457,
- } );
-
- expect( twoRecentBlocks ).toEqual( {
- insertUsage: {
- 'core-embed/twitter': {
- time: 123457,
- count: 2,
- insert: { name: 'core-embed/twitter' },
- },
- 'core/block/123': {
- time: 123457,
- count: 1,
- insert: { name: 'core/block', ref: 123 },
- },
- },
- } );
- } );
-
- it( 'should remove recorded reusable blocks that are deleted', () => {
- const initialState = {
- insertUsage: {
- 'core/block/123': {
- time: 1000,
- count: 1,
- insert: { name: 'core/block', ref: 123 },
- },
- },
- };
-
- const state = preferences( deepFreeze( initialState ), {
- type: 'REMOVE_REUSABLE_BLOCK',
- id: 123,
- } );
-
- expect( state ).toEqual( {
- insertUsage: {},
- } );
- } );
} );
describe( 'saving()', () => {
@@ -2071,28 +549,6 @@ describe( 'state', () => {
} );
} );
- describe( 'blocksMode', () => {
- it( 'should set mode to html if not set', () => {
- const action = {
- type: 'TOGGLE_BLOCK_MODE',
- clientId: 'chicken',
- };
- const value = blocksMode( deepFreeze( {} ), action );
-
- expect( value ).toEqual( { chicken: 'html' } );
- } );
-
- it( 'should toggle mode to visual if set as html', () => {
- const action = {
- type: 'TOGGLE_BLOCK_MODE',
- clientId: 'chicken',
- };
- const value = blocksMode( deepFreeze( { chicken: 'html' } ), action );
-
- expect( value ).toEqual( { chicken: 'visual' } );
- } );
- } );
-
describe( 'reusableBlocks()', () => {
it( 'should start out empty', () => {
const state = reusableBlocks( undefined, {} );
@@ -2346,153 +802,6 @@ describe( 'state', () => {
} );
} );
- describe( 'template', () => {
- it( 'should default to visible', () => {
- const state = template( undefined, {} );
-
- expect( state ).toEqual( { isValid: true } );
- } );
-
- it( 'should reset the validity flag', () => {
- const original = deepFreeze( { isValid: false, template: [] } );
- const state = template( original, {
- type: 'SET_TEMPLATE_VALIDITY',
- isValid: true,
- } );
-
- expect( state ).toEqual( { isValid: true, template: [] } );
- } );
- } );
-
- describe( 'blockListSettings', () => {
- it( 'should add new settings', () => {
- const original = deepFreeze( {} );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- settings: {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- expect( state ).toEqual( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
- } );
-
- it( 'should return same reference if updated as the same', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- settings: {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should return same reference if updated settings not assigned and id not exists', () => {
- const original = deepFreeze( {} );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- } );
-
- expect( state ).toBe( original );
- } );
-
- it( 'should update the settings of a block', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- settings: {
- allowedBlocks: [ 'core/list' ],
- },
- } );
-
- expect( state ).toEqual( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/list' ],
- },
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
- } );
-
- it( 'should remove existing settings if updated settings not assigned', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'UPDATE_BLOCK_LIST_SETTINGS',
- clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- } );
-
- expect( state ).toEqual( {} );
- } );
-
- it( 'should remove the settings of a block when it is replaced', () => {
- const original = deepFreeze( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'REPLACE_BLOCKS',
- clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
- } );
-
- expect( state ).toEqual( {
- '9db792c6-a25a-495d-adbd-97d56a4c4189': {
- allowedBlocks: [ 'core/paragraph' ],
- },
- } );
- } );
-
- it( 'should remove the settings of a block when it is removed', () => {
- const original = deepFreeze( {
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- allowedBlocks: true,
- },
- } );
-
- const state = blockListSettings( original, {
- type: 'REMOVE_BLOCKS',
- clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
- } );
-
- expect( state ).toEqual( {} );
- } );
- } );
-
describe( 'autosave', () => {
it( 'returns null by default', () => {
const state = autosave( undefined, {} );
diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js
index d75c81edbb909..459161ee03801 100644
--- a/packages/editor/src/store/test/selectors.js
+++ b/packages/editor/src/store/test/selectors.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { filter, without, omit } from 'lodash';
+import { filter, without } from 'lodash';
/**
* WordPress dependencies
@@ -13,6 +13,7 @@ import {
getDefaultBlockName,
setDefaultBlockName,
setFreeformContentHandlerName,
+ getBlockTypes,
} from '@wordpress/blocks';
import { RawHTML } from '@wordpress/element';
@@ -23,7 +24,6 @@ import * as selectors from '../selectors';
import { PREFERENCES_DEFAULTS } from '../defaults';
const {
- canUserUseUnfilteredHTML,
hasEditorUndo,
hasEditorRedo,
isEditedPostNew,
@@ -49,69 +49,27 @@ const {
isEditedPostEmpty,
isEditedPostBeingScheduled,
isEditedPostDateFloating,
- getBlockDependantsCacheBust,
- getBlockName,
- getBlock,
- getBlocks,
- getBlockCount,
- getClientIdsWithDescendants,
- getClientIdsOfDescendants,
- hasSelectedBlock,
- getSelectedBlock,
- getSelectedBlockClientId,
- getBlockRootClientId,
- getBlockHierarchyRootClientId,
getCurrentPostAttribute,
getEditedPostAttribute,
getAutosaveAttribute,
- getGlobalBlockCount,
- getMultiSelectedBlockClientIds,
- getMultiSelectedBlocks,
- getMultiSelectedBlocksStartClientId,
- getMultiSelectedBlocksEndClientId,
- getBlockOrder,
- getBlockIndex,
- getPreviousBlockClientId,
- getNextBlockClientId,
- isBlockSelected,
- hasSelectedInnerBlock,
- isBlockWithinSelection,
- hasMultiSelection,
- isBlockMultiSelected,
- isFirstMultiSelectedBlock,
- getBlockMode,
- isTyping,
- isCaretWithinFormattedText,
- getBlockInsertionPoint,
- isBlockInsertionPointVisible,
isSavingPost,
didPostSaveRequestSucceed,
didPostSaveRequestFail,
getSuggestedPostFormat,
- getBlocksForSerialization,
getEditedPostContent,
__experimentalGetReusableBlock: getReusableBlock,
__experimentalIsSavingReusableBlock: isSavingReusableBlock,
__experimentalIsFetchingReusableBlock: isFetchingReusableBlock,
- isSelectionEnabled,
__experimentalGetReusableBlocks: getReusableBlocks,
getStateBeforeOptimisticTransaction,
isPublishingPost,
isPublishSidebarEnabled,
- canInsertBlockType,
- getInserterItems,
- isValidTemplate,
- getTemplate,
- getTemplateLock,
- getBlockListSettings,
POST_UPDATE_TRANSACTION_ID,
isPermalinkEditable,
getPermalink,
getPermalinkParts,
- INSERTER_UTILITY_HIGH,
- INSERTER_UTILITY_MEDIUM,
- INSERTER_UTILITY_LOW,
isPostSavingLocked,
+ canUserUseUnfilteredHTML,
} = selectors;
describe( 'selectors', () => {
@@ -309,6 +267,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: true,
+ value: [],
},
edits: {},
},
@@ -324,6 +283,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {
content: 'text mode edited',
@@ -344,6 +304,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -360,6 +321,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: true,
+ value: [],
},
edits: {},
},
@@ -376,6 +338,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {
excerpt: 'hello world',
@@ -396,6 +359,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: true,
+ value: [],
},
edits: {},
},
@@ -407,6 +371,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -424,6 +389,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -446,6 +412,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -468,6 +435,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: true,
+ value: [],
},
edits: {},
},
@@ -994,6 +962,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -1015,6 +984,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -1036,6 +1006,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -1057,6 +1028,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: true,
+ value: [],
},
edits: {},
},
@@ -1078,6 +1050,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -1099,6 +1072,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -1123,6 +1097,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: true,
+ value: [],
},
edits: {},
},
@@ -1164,9 +1139,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {},
},
@@ -1184,9 +1157,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {},
},
@@ -1208,9 +1179,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {},
},
@@ -1233,6 +1202,7 @@ describe( 'selectors', () => {
byClientId: {},
attributes: {},
order: {},
+ value: [],
},
edits: {},
},
@@ -1252,21 +1222,16 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
+ value: [
+ {
clientId: 123,
name: 'core/test-block-a',
isValid: true,
+ attributes: {
+ text: '',
+ },
},
- },
- attributes: {
- 123: {
- text: '',
- },
- },
- order: {
- '': [ 123 ],
- },
+ ],
},
edits: {},
},
@@ -1284,20 +1249,16 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
+ value: [
+ {
clientId: 123,
name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
+ content: '',
+ },
},
- },
- attributes: {
- 123: {
- content: '',
- },
- },
- order: {
- '': [ 123 ],
- },
+ ],
},
edits: {},
},
@@ -1315,21 +1276,16 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
+ value: [
+ {
clientId: 123,
name: 'core/test-freeform',
isValid: true,
+ attributes: {
+ content: '',
+ },
},
- },
- attributes: {
- 123: {
- content: '',
- },
- },
- order: {
- '': [ 123 ],
- },
+ ],
},
edits: {},
},
@@ -1351,9 +1307,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {},
},
@@ -1378,9 +1332,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {},
},
@@ -1401,6 +1353,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
+ value: [],
isDirty: false,
},
edits: {},
@@ -1426,6 +1379,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
+ value: [],
isDirty: true,
},
edits: {},
@@ -1453,6 +1407,7 @@ describe( 'selectors', () => {
present: {
blocks: {
isDirty: false,
+ value: [],
},
edits: {},
},
@@ -1524,9 +1479,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {},
},
@@ -1543,21 +1496,14 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-block-a',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-block-a',
+ isValid: true,
+ attributes: {
text: '',
},
- },
- order: {
- '': [ 123 ],
- },
+ } ],
},
edits: {},
},
@@ -1579,20 +1525,13 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- },
- attributes: {
- block1: {
+ value: [ {
+ clientId: 'block1',
+ name: 'core/test-default',
+ attributes: {
modified: false,
},
- },
- order: {
- '': [ 'block1' ],
- },
+ } ],
},
edits: {},
},
@@ -1609,21 +1548,14 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-block-a',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-block-a',
+ isValid: true,
+ attributes: {
text: '',
},
- },
- order: {
- '': [ 123 ],
- },
+ } ],
},
edits: {
content: '',
@@ -1644,9 +1576,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {},
},
@@ -1665,9 +1595,7 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [],
},
edits: {
content: 'sassel',
@@ -1686,21 +1614,14 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
content: '',
},
- },
- order: {
- '': [ 123 ],
- },
+ } ],
},
edits: {},
},
@@ -1717,21 +1638,14 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
content: '',
},
- },
- order: {
- '': [ 123 ],
- },
+ } ],
},
edits: {},
},
@@ -1750,21 +1664,14 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
- clientId: 123,
- name: 'core/test-freeform',
- isValid: true,
- },
- },
- attributes: {
- 123: {
+ value: [ {
+ clientId: 123,
+ name: 'core/test-freeform',
+ isValid: true,
+ attributes: {
content: 'Test Data',
},
- },
- order: {
- '': [ 123 ],
- },
+ } ],
},
edits: {},
},
@@ -1783,29 +1690,24 @@ describe( 'selectors', () => {
editor: {
present: {
blocks: {
- byClientId: {
- 123: {
+ value: [
+ {
clientId: 123,
name: 'core/test-freeform',
isValid: true,
+ attributes: {
+ content: '',
+ },
},
- 456: {
+ {
clientId: 456,
name: 'core/test-freeform',
isValid: true,
+ attributes: {
+ content: '',
+ },
},
- },
- attributes: {
- 123: {
- content: '',
- },
- 456: {
- content: '',
- },
- },
- order: {
- '': [ 123, 456 ],
- },
+ ],
},
edits: {},
},
@@ -1960,3038 +1862,378 @@ describe( 'selectors', () => {
} );
} );
- describe( 'getBlockDependantsCacheBust', () => {
- const rootBlock = { clientId: 123, name: 'core/paragraph' };
- const rootBlockAttributes = {};
- const rootOrder = [ 123 ];
+ describe( 'isSavingPost', () => {
+ it( 'should return true if the post is currently being saved', () => {
+ const state = {
+ saving: {
+ requesting: true,
+ },
+ };
- it( 'returns an unchanging reference', () => {
- const rootBlockOrder = [];
+ expect( isSavingPost( state ) ).toBe( true );
+ } );
+ it( 'should return false if the post is not currently being saved', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- },
- },
- edits: {},
- },
+ saving: {
+ requesting: false,
},
- initialEdits: {},
};
- const nextState = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- },
- },
- edits: {},
- },
+ expect( isSavingPost( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'didPostSaveRequestSucceed', () => {
+ it( 'should return true if the post save request is successful', () => {
+ const state = {
+ saving: {
+ successful: true,
},
- initialEdits: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( didPostSaveRequestSucceed( state ) ).toBe( true );
} );
- it( 'returns a new reference on added inner block', () => {
+ it( 'should return true if the post save request has failed', () => {
const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: [],
- },
- },
- edits: {},
- },
+ saving: {
+ successful: false,
},
- initialEdits: {},
};
- const nextState = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: rootBlock,
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: {},
- },
- order: {
- '': rootOrder,
- 123: [ 456 ],
- 456: [],
- },
- },
- edits: {},
- },
+ expect( didPostSaveRequestSucceed( state ) ).toBe( false );
+ } );
+ } );
+
+ describe( 'didPostSaveRequestFail', () => {
+ it( 'should return true if the post save request has failed', () => {
+ const state = {
+ saving: {
+ error: 'error',
},
- initialEdits: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( didPostSaveRequestFail( state ) ).toBe( true );
} );
- it( 'returns an unchanging reference on unchanging inner block', () => {
- const rootBlockOrder = [ 456 ];
- const childBlock = { clientId: 456, name: 'core/paragraph' };
- const childBlockAttributes = {};
- const childBlockOrder = [];
+ it( 'should return true if the post save request is successful', () => {
+ const state = {
+ saving: {
+ error: false,
+ },
+ };
+
+ expect( didPostSaveRequestFail( state ) ).toBe( false );
+ } );
+ } );
+ describe( 'getSuggestedPostFormat', () => {
+ it( 'returns null if cannot be determined', () => {
const state = {
- currentPost: {},
editor: {
present: {
blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
+ value: [],
},
edits: {},
},
},
initialEdits: {},
+ currentPost: {},
};
- const nextState = {
- currentPost: {},
+ expect( getSuggestedPostFormat( state ) ).toBeNull();
+ } );
+
+ it( 'returns null if there is more than one block in the post', () => {
+ const state = {
editor: {
present: {
blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
+ value: [
+ {
+ clientId: 123,
+ name: 'core/image',
+ attributes: {},
+ },
+ {
+ clientId: 456,
+ name: 'core/quote',
+ attributes: {},
+ },
+ ],
},
edits: {},
},
},
initialEdits: {},
+ currentPost: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( getSuggestedPostFormat( state ) ).toBeNull();
} );
- it( 'returns a new reference on updated inner block', () => {
- const rootBlockOrder = [ 456 ];
- const childBlockOrder = [];
-
+ it( 'returns Image if the first block is of type `core/image`', () => {
const state = {
- currentPost: {},
editor: {
present: {
blocks: {
- byClientId: {
- 123: rootBlock,
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: {},
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
+ value: [
+ {
+ clientId: 123,
+ name: 'core/image',
+ attributes: {},
+ },
+ ],
},
edits: {},
},
},
initialEdits: {},
+ currentPost: {},
};
- const nextState = {
- currentPost: {},
+ expect( getSuggestedPostFormat( state ) ).toBe( 'image' );
+ } );
+
+ it( 'returns Quote if the first block is of type `core/quote`', () => {
+ const state = {
editor: {
present: {
blocks: {
- byClientId: {
- 123: rootBlock,
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: { content: [ 'foo' ] },
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- },
+ value: [
+ {
+ clientId: 456,
+ name: 'core/quote',
+ attributes: {},
+ },
+ ],
},
edits: {},
},
},
initialEdits: {},
+ currentPost: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
} );
- it( 'returns a new reference on updated grandchild inner block', () => {
- const rootBlockOrder = [ 456 ];
- const childBlock = { clientId: 456, name: 'core/paragraph' };
- const childBlockAttributes = {};
- const childBlockOrder = [ 789 ];
- const grandChildBlockOrder = [];
-
+ it( 'returns Video if the first block is of type `core-embed/youtube`', () => {
const state = {
- currentPost: {},
editor: {
present: {
blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- 789: {},
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- 789: grandChildBlockOrder,
- },
+ value: [
+ {
+ clientId: 567,
+ name: 'core-embed/youtube',
+ attributes: {},
+ },
+ ],
},
edits: {},
},
},
initialEdits: {},
+ currentPost: {},
};
- const nextState = {
- currentPost: {},
+ expect( getSuggestedPostFormat( state ) ).toBe( 'video' );
+ } );
+
+ it( 'returns Quote if the first block is of type `core/quote` and second is of type `core/paragraph`', () => {
+ const state = {
editor: {
present: {
blocks: {
- byClientId: {
- 123: rootBlock,
- 456: childBlock,
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: rootBlockAttributes,
- 456: childBlockAttributes,
- 789: { content: [ 'foo' ] },
- },
- order: {
- '': rootOrder,
- 123: rootBlockOrder,
- 456: childBlockOrder,
- 789: grandChildBlockOrder,
- },
+ value: [
+ {
+ clientId: 456,
+ name: 'core/quote',
+ attributes: {},
+ },
+ {
+ clientId: 789,
+ name: 'core/paragraph',
+ attributes: {},
+ },
+ ],
},
edits: {},
},
},
initialEdits: {},
+ currentPost: {},
};
- expect(
- getBlockDependantsCacheBust( state, 123 )
- ).not.toBe( getBlockDependantsCacheBust( nextState, 123 ) );
+ expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
} );
} );
- describe( 'getBlockName', () => {
- it( 'returns null if no block by clientId', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
-
- expect( name ).toBe( null );
- } );
-
- it( 'returns block name', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {
- clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- name: 'core/paragraph',
- },
- },
- attributes: {
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': {},
- },
- order: {
- '': [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ],
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- const name = getBlockName( state, 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' );
-
- expect( name ).toBe( 'core/paragraph' );
- } );
- } );
-
- describe( 'getBlock', () => {
- it( 'should return the block', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- },
- order: {
- '': [ 123 ],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- expect( getBlock( state, 123 ) ).toEqual( {
- clientId: 123,
- name: 'core/paragraph',
- attributes: {},
- innerBlocks: [],
- } );
- } );
-
- it( 'should return null if the block is not present in state', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- expect( getBlock( state, 123 ) ).toBe( null );
- } );
-
- it( 'should include inner blocks', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/paragraph' },
- 456: { clientId: 456, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- 456: {},
- },
- order: {
- '': [ 123 ],
- 123: [ 456 ],
- 456: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- expect( getBlock( state, 123 ) ).toEqual( {
- clientId: 123,
- name: 'core/paragraph',
- attributes: {},
- innerBlocks: [ {
- clientId: 456,
- name: 'core/paragraph',
- attributes: {},
- innerBlocks: [],
- } ],
- } );
- } );
-
- it( 'should merge meta attributes for the block', () => {
- registerBlockType( 'core/meta-block', {
- save: ( props ) => props.attributes.text,
- category: 'common',
- title: 'test block',
- attributes: {
- foo: {
- type: 'string',
- source: 'meta',
- meta: 'foo',
- },
- },
- } );
-
- const state = {
- currentPost: {
- meta: {
- foo: 'bar',
- },
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/meta-block' },
- },
- attributes: {
- 123: {},
- },
- order: {
- '': [ 123 ],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- expect( getBlock( state, 123 ) ).toEqual( {
- clientId: 123,
- name: 'core/meta-block',
- attributes: {
- foo: 'bar',
- },
- innerBlocks: [],
- } );
-
- unregisterBlockType( 'core/meta-block' );
- } );
- } );
-
- describe( 'getBlocks', () => {
- it( 'should return the ordered blocks', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 123, 23 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
-
- expect( getBlocks( state ) ).toEqual( [
- { clientId: 123, name: 'core/paragraph', attributes: {}, innerBlocks: [] },
- { clientId: 23, name: 'core/heading', attributes: {}, innerBlocks: [] },
- ] );
- } );
- } );
-
- describe( 'getClientIdsOfDescendants', () => {
- it( 'should return the ids of any descendants, given an array of clientIds', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
- 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
- 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
- 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
- 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
- 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
- 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
- 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
- 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
- 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
- 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
- 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
- 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
- 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
- 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
- },
- attributes: {
- 'uuid-2': {},
- 'uuid-4': {},
- 'uuid-6': {},
- 'uuid-8': {},
- 'uuid-10': {},
- 'uuid-12': {},
- 'uuid-14': {},
- 'uuid-16': {},
- 'uuid-18': {},
- 'uuid-20': {},
- 'uuid-22': {},
- 'uuid-24': {},
- 'uuid-26': {},
- 'uuid-28': {},
- 'uuid-30': {},
- },
- order: {
- '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
- 'uuid-2': [ ],
- 'uuid-4': [ ],
- 'uuid-6': [ ],
- 'uuid-8': [ ],
- 'uuid-10': [ 'uuid-12', 'uuid-14' ],
- 'uuid-12': [ 'uuid-16' ],
- 'uuid-14': [ 'uuid-18' ],
- 'uuid-16': [ ],
- 'uuid-18': [ 'uuid-24' ],
- 'uuid-20': [ ],
- 'uuid-22': [ ],
- 'uuid-24': [ 'uuid-26', 'uuid-28' ],
- 'uuid-26': [ ],
- 'uuid-28': [ 'uuid-30' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
- expect( getClientIdsOfDescendants( state, [ 'uuid-10' ] ) ).toEqual( [
- 'uuid-12',
- 'uuid-14',
- 'uuid-16',
- 'uuid-18',
- 'uuid-24',
- 'uuid-26',
- 'uuid-28',
- 'uuid-30',
- ] );
- } );
- } );
-
- describe( 'getClientIdsWithDescendants', () => {
- it( 'should return the ids for top-level blocks and their descendants of any depth (for nested blocks).', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 'uuid-2': { clientId: 'uuid-2', name: 'core/image' },
- 'uuid-4': { clientId: 'uuid-4', name: 'core/paragraph' },
- 'uuid-6': { clientId: 'uuid-6', name: 'core/paragraph' },
- 'uuid-8': { clientId: 'uuid-8', name: 'core/block' },
- 'uuid-10': { clientId: 'uuid-10', name: 'core/columns' },
- 'uuid-12': { clientId: 'uuid-12', name: 'core/column' },
- 'uuid-14': { clientId: 'uuid-14', name: 'core/column' },
- 'uuid-16': { clientId: 'uuid-16', name: 'core/quote' },
- 'uuid-18': { clientId: 'uuid-18', name: 'core/block' },
- 'uuid-20': { clientId: 'uuid-20', name: 'core/gallery' },
- 'uuid-22': { clientId: 'uuid-22', name: 'core/block' },
- 'uuid-24': { clientId: 'uuid-24', name: 'core/columns' },
- 'uuid-26': { clientId: 'uuid-26', name: 'core/column' },
- 'uuid-28': { clientId: 'uuid-28', name: 'core/column' },
- 'uuid-30': { clientId: 'uuid-30', name: 'core/paragraph' },
- },
- attributes: {
- 'uuid-2': {},
- 'uuid-4': {},
- 'uuid-6': {},
- 'uuid-8': {},
- 'uuid-10': {},
- 'uuid-12': {},
- 'uuid-14': {},
- 'uuid-16': {},
- 'uuid-18': {},
- 'uuid-20': {},
- 'uuid-22': {},
- 'uuid-24': {},
- 'uuid-26': {},
- 'uuid-28': {},
- 'uuid-30': {},
- },
- order: {
- '': [ 'uuid-6', 'uuid-8', 'uuid-10', 'uuid-22' ],
- 'uuid-2': [ ],
- 'uuid-4': [ ],
- 'uuid-6': [ ],
- 'uuid-8': [ ],
- 'uuid-10': [ 'uuid-12', 'uuid-14' ],
- 'uuid-12': [ 'uuid-16' ],
- 'uuid-14': [ 'uuid-18' ],
- 'uuid-16': [ ],
- 'uuid-18': [ 'uuid-24' ],
- 'uuid-20': [ ],
- 'uuid-22': [ ],
- 'uuid-24': [ 'uuid-26', 'uuid-28' ],
- 'uuid-26': [ ],
- 'uuid-28': [ 'uuid-30' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- };
- expect( getClientIdsWithDescendants( state ) ).toEqual( [
- 'uuid-6',
- 'uuid-8',
- 'uuid-10',
- 'uuid-22',
- 'uuid-12',
- 'uuid-14',
- 'uuid-16',
- 'uuid-18',
- 'uuid-24',
- 'uuid-26',
- 'uuid-28',
- 'uuid-30',
- ] );
- } );
- } );
-
- describe( 'getBlockCount', () => {
- it( 'should return the number of top-level blocks in the post', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getBlockCount( state ) ).toBe( 2 );
- } );
-
- it( 'should return the number of blocks in a nested context', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/columns' },
- 456: { clientId: 456, name: 'core/paragraph' },
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- 456: {},
- 789: {},
- },
- order: {
- '': [ 123 ],
- 123: [ 456, 789 ],
- },
- },
- },
- },
- };
-
- expect( getBlockCount( state, '123' ) ).toBe( 2 );
- } );
- } );
-
- describe( 'hasSelectedBlock', () => {
- it( 'should return false if no selection', () => {
- const state = {
- blockSelection: {
- start: null,
- end: null,
- },
- };
-
- expect( hasSelectedBlock( state ) ).toBe( false );
- } );
-
- it( 'should return false if multi-selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- },
- };
-
- expect( hasSelectedBlock( state ) ).toBe( false );
- } );
-
- it( 'should return true if singular selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- },
- };
-
- expect( hasSelectedBlock( state ) ).toBe( true );
- } );
- } );
-
- describe( 'getGlobalBlockCount', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/heading' },
- 456: { clientId: 456, name: 'core/paragraph' },
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 123: {},
- 456: {},
- 789: {},
- },
- order: {
- '': [ 123, 456 ],
- },
- },
- },
- },
- };
-
- it( 'should return the global number of blocks in the post', () => {
- expect( getGlobalBlockCount( state ) ).toBe( 2 );
- } );
-
- it( 'should return the global number of blocks in the post of a given type', () => {
- expect( getGlobalBlockCount( state, 'core/paragraph' ) ).toBe( 1 );
- } );
-
- it( 'should return 0 if no blocks exist', () => {
- const emptyState = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- },
- },
- };
- expect( getGlobalBlockCount( emptyState ) ).toBe( 0 );
- expect( getGlobalBlockCount( emptyState, 'core/heading' ) ).toBe( 0 );
- } );
- } );
-
- describe( 'getSelectedBlockClientId', () => {
- it( 'should return null if no block is selected', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( getSelectedBlockClientId( state ) ).toBe( null );
- } );
-
- it( 'should return null if there is multi selection', () => {
- const state = {
- blockSelection: { start: 23, end: 123 },
- };
-
- expect( getSelectedBlockClientId( state ) ).toBe( null );
- } );
-
- it( 'should return the selected block ClientId', () => {
- const state = {
- editor: { present: { blocks: { byClientId: { 23: { name: 'fake block' } } } } },
- blockSelection: { start: 23, end: 23 },
- };
-
- expect( getSelectedBlockClientId( state ) ).toEqual( 23 );
- } );
- } );
-
- describe( 'getSelectedBlock', () => {
- it( 'should return null if no block is selected', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 23, 123 ],
- 23: [],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: null, end: null },
- };
-
- expect( getSelectedBlock( state ) ).toBe( null );
- } );
-
- it( 'should return null if there is multi selection', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 23, 123 ],
- 23: [],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: 23, end: 123 },
- };
-
- expect( getSelectedBlock( state ) ).toBe( null );
- } );
-
- it( 'should return the selected block', () => {
- const state = {
- currentPost: {},
- editor: {
- present: {
- blocks: {
- byClientId: {
- 23: { clientId: 23, name: 'core/heading' },
- 123: { clientId: 123, name: 'core/paragraph' },
- },
- attributes: {
- 23: {},
- 123: {},
- },
- order: {
- '': [ 23, 123 ],
- 23: [],
- 123: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: 23, end: 23 },
- };
-
- expect( getSelectedBlock( state ) ).toEqual( {
- clientId: 23,
- name: 'core/heading',
- attributes: {},
- innerBlocks: [],
- } );
- } );
- } );
-
- describe( 'getBlockRootClientId', () => {
- it( 'should return null if the block does not exist', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {},
- },
- },
- },
- };
-
- expect( getBlockRootClientId( state, 56 ) ).toBeNull();
- } );
-
- it( 'should return root ClientId relative the block ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getBlockRootClientId( state, 56 ) ).toBe( '123' );
- } );
- } );
-
- describe( 'getBlockHierarchyRootClientId', () => {
- it( 'should return the given block if the block has no parents', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {},
- },
- },
- },
- };
-
- expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( 56 );
- } );
-
- it( 'should return root ClientId relative the block ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getBlockHierarchyRootClientId( state, 56 ) ).toBe( '123' );
- } );
-
- it( 'should return the top level root ClientId relative the block ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ '123', '23' ],
- 123: [ '456', '56' ],
- 56: [ '12' ],
- },
- },
- },
- },
- };
-
- expect( getBlockHierarchyRootClientId( state, '12' ) ).toBe( '123' );
- } );
- } );
-
- describe( 'getMultiSelectedBlockClientIds', () => {
- it( 'should return empty if there is no multi selection', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- blockSelection: { start: null, end: null },
- };
-
- expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [] );
- } );
-
- it( 'should return selected block clientIds if there is multi selection', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
-
- expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 4, 3, 2 ] );
- } );
-
- it( 'should return selected block clientIds if there is multi selection (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- 4: [ 9, 8, 7, 6 ],
- },
- },
- },
- },
- blockSelection: { start: 7, end: 9 },
- };
-
- expect( getMultiSelectedBlockClientIds( state ) ).toEqual( [ 9, 8, 7 ] );
- } );
- } );
-
- describe( 'getMultiSelectedBlocks', () => {
- it( 'should return the same reference on subsequent invocations of empty selection', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- blockSelection: { start: null, end: null },
- currentPost: {},
- };
-
- expect(
- getMultiSelectedBlocks( state )
- ).toBe( getMultiSelectedBlocks( state ) );
- } );
- } );
-
- describe( 'getMultiSelectedBlocksStartClientId', () => {
- it( 'returns null if there is no multi selection', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( getMultiSelectedBlocksStartClientId( state ) ).toBeNull();
- } );
-
- it( 'returns multi selection start', () => {
- const state = {
- blockSelection: { start: 2, end: 4 },
- };
-
- expect( getMultiSelectedBlocksStartClientId( state ) ).toBe( 2 );
- } );
- } );
-
- describe( 'getMultiSelectedBlocksEndClientId', () => {
- it( 'returns null if there is no multi selection', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( getMultiSelectedBlocksEndClientId( state ) ).toBeNull();
- } );
-
- it( 'returns multi selection end', () => {
- const state = {
- blockSelection: { start: 2, end: 4 },
- };
-
- expect( getMultiSelectedBlocksEndClientId( state ) ).toBe( 4 );
- } );
- } );
-
- describe( 'getBlockOrder', () => {
- it( 'should return the ordered block ClientIds of top-level blocks by default', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getBlockOrder( state ) ).toEqual( [ 123, 23 ] );
- } );
-
- it( 'should return the ordered block ClientIds at a specified rootClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456 ],
- },
- },
- },
- },
- };
-
- expect( getBlockOrder( state, '123' ) ).toEqual( [ 456 ] );
- } );
- } );
-
- describe( 'getBlockIndex', () => {
- it( 'should return the block order', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getBlockIndex( state, 23 ) ).toBe( 1 );
- } );
-
- it( 'should return the block order (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getBlockIndex( state, 56, '123' ) ).toBe( 1 );
- } );
- } );
-
- describe( 'getPreviousBlockClientId', () => {
- it( 'should return the previous block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 23 ) ).toEqual( 123 );
- } );
-
- it( 'should return the previous block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 56, '123' ) ).toEqual( 456 );
- } );
-
- it( 'should return null for the first block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 123 ) ).toBeNull();
- } );
-
- it( 'should return null for the first block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getPreviousBlockClientId( state, 456, '123' ) ).toBeNull();
- } );
- } );
-
- describe( 'getNextBlockClientId', () => {
- it( 'should return the following block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 123 ) ).toEqual( 23 );
- } );
-
- it( 'should return the following block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 456, '123' ) ).toEqual( 56 );
- } );
-
- it( 'should return null for the last block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 23 ) ).toBeNull();
- } );
-
- it( 'should return null for the last block (nested context)', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 123, 23 ],
- 123: [ 456, 56 ],
- },
- },
- },
- },
- };
-
- expect( getNextBlockClientId( state, 56, '123' ) ).toBeNull();
- } );
- } );
-
- describe( 'isBlockSelected', () => {
- it( 'should return true if the block is selected', () => {
- const state = {
- blockSelection: { start: 123, end: 123 },
- };
-
- expect( isBlockSelected( state, 123 ) ).toBe( true );
- } );
-
- it( 'should return false if a multi-selection range exists', () => {
- const state = {
- blockSelection: { start: 123, end: 124 },
- };
-
- expect( isBlockSelected( state, 123 ) ).toBe( false );
- } );
-
- it( 'should return false if the block is not selected', () => {
- const state = {
- blockSelection: { start: null, end: null },
- };
-
- expect( isBlockSelected( state, 23 ) ).toBe( false );
- } );
- } );
-
- describe( 'hasSelectedInnerBlock', () => {
- it( 'should return false if the selected block is a child of the given ClientId', () => {
- const state = {
- blockSelection: { start: 5, end: 5 },
- editor: {
- present: {
- blocks: {
- order: {
- 4: [ 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( hasSelectedInnerBlock( state, 4 ) ).toBe( false );
- } );
-
- it( 'should return true if the selected block is a child of the given ClientId', () => {
- const state = {
- blockSelection: { start: 3, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- 4: [ 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( hasSelectedInnerBlock( state, 4 ) ).toBe( true );
- } );
-
- it( 'should return true if a multi selection exists that contains children of the block with the given ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- 6: [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
- expect( hasSelectedInnerBlock( state, 6 ) ).toBe( true );
- } );
-
- it( 'should return false if a multi selection exists bot does not contains children of the block with the given ClientId', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- 3: [ 2, 1 ],
- 6: [ 5, 4 ],
- },
- },
- },
- },
- blockSelection: { start: 5, end: 4 },
- };
- expect( hasSelectedInnerBlock( state, 3 ) ).toBe( false );
- } );
- } );
-
- describe( 'isBlockWithinSelection', () => {
- it( 'should return true if the block is selected but not the last', () => {
- const state = {
- blockSelection: { start: 5, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 4 ) ).toBe( true );
- } );
-
- it( 'should return false if the block is the last selected', () => {
- const state = {
- blockSelection: { start: 5, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 3 ) ).toBe( false );
- } );
-
- it( 'should return false if the block is not selected', () => {
- const state = {
- blockSelection: { start: 5, end: 3 },
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 2 ) ).toBe( false );
- } );
-
- it( 'should return false if there is no selection', () => {
- const state = {
- blockSelection: {},
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- };
-
- expect( isBlockWithinSelection( state, 4 ) ).toBe( false );
- } );
- } );
-
- describe( 'hasMultiSelection', () => {
- it( 'should return false if no selection', () => {
- const state = {
- blockSelection: {
- start: null,
- end: null,
- },
- };
-
- expect( hasMultiSelection( state ) ).toBe( false );
- } );
-
- it( 'should return false if singular selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- },
- };
-
- expect( hasMultiSelection( state ) ).toBe( false );
- } );
-
- it( 'should return true if multi-selection', () => {
- const state = {
- blockSelection: {
- start: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1',
- end: '9db792c6-a25a-495d-adbd-97d56a4c4189',
- },
- };
-
- expect( hasMultiSelection( state ) ).toBe( true );
- } );
- } );
-
- describe( 'isBlockMultiSelected', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
-
- it( 'should return true if the block is multi selected', () => {
- expect( isBlockMultiSelected( state, 3 ) ).toBe( true );
- } );
-
- it( 'should return false if the block is not multi selected', () => {
- expect( isBlockMultiSelected( state, 5 ) ).toBe( false );
- } );
- } );
-
- describe( 'isFirstMultiSelectedBlock', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- order: {
- '': [ 5, 4, 3, 2, 1 ],
- },
- },
- },
- },
- blockSelection: { start: 2, end: 4 },
- };
-
- it( 'should return true if the block is first in multi selection', () => {
- expect( isFirstMultiSelectedBlock( state, 4 ) ).toBe( true );
- } );
-
- it( 'should return false if the block is not first in multi selection', () => {
- expect( isFirstMultiSelectedBlock( state, 3 ) ).toBe( false );
- } );
- } );
-
- describe( 'getBlockMode', () => {
- it( 'should return "visual" if unset', () => {
- const state = {
- blocksMode: {},
- };
-
- expect( getBlockMode( state, 123 ) ).toEqual( 'visual' );
- } );
-
- it( 'should return the block mode', () => {
- const state = {
- blocksMode: {
- 123: 'html',
- },
- };
-
- expect( getBlockMode( state, 123 ) ).toEqual( 'html' );
- } );
- } );
-
- describe( 'isTyping', () => {
- it( 'should return the isTyping flag if the block is selected', () => {
- const state = {
- isTyping: true,
- };
-
- expect( isTyping( state ) ).toBe( true );
- } );
-
- it( 'should return false if the block is not selected', () => {
- const state = {
- isTyping: false,
- };
-
- expect( isTyping( state ) ).toBe( false );
- } );
- } );
-
- describe( 'isCaretWithinFormattedText', () => {
- it( 'returns true if the isCaretWithinFormattedText state is also true', () => {
- const state = {
- isCaretWithinFormattedText: true,
- };
-
- expect( isCaretWithinFormattedText( state ) ).toBe( true );
- } );
-
- it( 'returns false if the isCaretWithinFormattedText state is also false', () => {
- const state = {
- isCaretWithinFormattedText: false,
- };
-
- expect( isCaretWithinFormattedText( state ) ).toBe( false );
- } );
- } );
-
- describe( 'isSelectionEnabled', () => {
- it( 'should return true if selection is enable', () => {
- const state = {
- blockSelection: {
- isEnabled: true,
- },
- };
-
- expect( isSelectionEnabled( state ) ).toBe( true );
- } );
-
- it( 'should return false if selection is disabled', () => {
- const state = {
- blockSelection: {
- isEnabled: false,
- },
- };
-
- expect( isSelectionEnabled( state ) ).toBe( false );
- } );
- } );
-
- describe( 'getBlockInsertionPoint', () => {
- it( 'should return the explicitly assigned insertion point', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId2',
- end: 'clientId2',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1' ],
- clientId1: [ 'clientId2' ],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: {
- rootClientId: undefined,
- index: 0,
- },
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 0,
- } );
- } );
-
- it( 'should return an object for the selected block', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId1',
- end: 'clientId1',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- },
- attributes: {
- clientId1: {},
- },
- order: {
- '': [ 'clientId1' ],
- clientId1: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 1,
- } );
- } );
-
- it( 'should return an object for the nested selected block', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId2',
- end: 'clientId2',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1' ],
- clientId1: [ 'clientId2' ],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: 'clientId1',
- index: 1,
- } );
- } );
-
- it( 'should return an object for the last multi selected clientId', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: 'clientId1',
- end: 'clientId2',
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1', 'clientId2' ],
- clientId1: [],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 2,
- } );
- } );
-
- it( 'should return an object for the last block if no selection', () => {
- const state = {
- currentPost: {},
- preferences: { mode: 'visual' },
- blockSelection: {
- start: null,
- end: null,
- },
- editor: {
- present: {
- blocks: {
- byClientId: {
- clientId1: { clientId: 'clientId1' },
- clientId2: { clientId: 'clientId2' },
- },
- attributes: {
- clientId1: {},
- clientId2: {},
- },
- order: {
- '': [ 'clientId1', 'clientId2' ],
- clientId1: [],
- clientId2: [],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- insertionPoint: null,
- };
-
- expect( getBlockInsertionPoint( state ) ).toEqual( {
- rootClientId: undefined,
- index: 2,
- } );
- } );
- } );
-
- describe( 'isBlockInsertionPointVisible', () => {
- it( 'should return false if no assigned insertion point', () => {
- const state = {
- insertionPoint: null,
- };
-
- expect( isBlockInsertionPointVisible( state ) ).toBe( false );
- } );
-
- it( 'should return true if assigned insertion point', () => {
- const state = {
- insertionPoint: {
- rootClientId: undefined,
- index: 5,
- },
- };
-
- expect( isBlockInsertionPointVisible( state ) ).toBe( true );
- } );
- } );
-
- describe( 'isSavingPost', () => {
- it( 'should return true if the post is currently being saved', () => {
- const state = {
- saving: {
- requesting: true,
- },
- };
-
- expect( isSavingPost( state ) ).toBe( true );
- } );
-
- it( 'should return false if the post is not currently being saved', () => {
- const state = {
- saving: {
- requesting: false,
- },
- };
-
- expect( isSavingPost( state ) ).toBe( false );
- } );
- } );
-
- describe( 'didPostSaveRequestSucceed', () => {
- it( 'should return true if the post save request is successful', () => {
- const state = {
- saving: {
- successful: true,
- },
- };
-
- expect( didPostSaveRequestSucceed( state ) ).toBe( true );
- } );
-
- it( 'should return true if the post save request has failed', () => {
- const state = {
- saving: {
- successful: false,
- },
- };
-
- expect( didPostSaveRequestSucceed( state ) ).toBe( false );
- } );
- } );
-
- describe( 'didPostSaveRequestFail', () => {
- it( 'should return true if the post save request has failed', () => {
- const state = {
- saving: {
- error: 'error',
- },
- };
-
- expect( didPostSaveRequestFail( state ) ).toBe( true );
- } );
-
- it( 'should return true if the post save request is successful', () => {
- const state = {
- saving: {
- error: false,
- },
- };
-
- expect( didPostSaveRequestFail( state ) ).toBe( false );
- } );
- } );
-
- describe( 'getSuggestedPostFormat', () => {
- it( 'returns null if cannot be determined', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBeNull();
- } );
-
- it( 'returns null if there is more than one block in the post', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/image' },
- 456: { clientId: 456, name: 'core/quote' },
- },
- attributes: {
- 123: {},
- 456: {},
- },
- order: {
- '': [ 123, 456 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBeNull();
- } );
-
- it( 'returns Image if the first block is of type `core/image`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 123: { clientId: 123, name: 'core/image' },
- },
- attributes: {
- 123: {},
- },
- order: {
- '': [ 123 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'image' );
- } );
-
- it( 'returns Quote if the first block is of type `core/quote`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 456: { clientId: 456, name: 'core/quote' },
- },
- attributes: {
- 456: {},
- },
- order: {
- '': [ 456 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
- } );
-
- it( 'returns Video if the first block is of type `core-embed/youtube`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 567: { clientId: 567, name: 'core-embed/youtube' },
- },
- attributes: {
- 567: {},
- },
- order: {
- '': [ 567 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'video' );
- } );
-
- it( 'returns Quote if the first block is of type `core/quote` and second is of type `core/paragraph`', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- 456: { clientId: 456, name: 'core/quote' },
- 789: { clientId: 789, name: 'core/paragraph' },
- },
- attributes: {
- 456: {},
- 789: {},
- },
- order: {
- '': [ 456, 789 ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getSuggestedPostFormat( state ) ).toBe( 'quote' );
- } );
- } );
-
- describe( 'getBlocksForSerialization', () => {
- it( 'should return blocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- block2: {
- clientId: 'block2',
- name: 'core/heading',
- },
- },
- attributes: {
- block1: {
- modified: false,
- },
- block2: {},
- },
- order: {
- '': [ 'block1', 'block2' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getBlocksForSerialization( state ) ).toEqual( [
- { clientId: 'block1', name: 'core/test-default', attributes: { modified: false }, innerBlocks: [] },
- { clientId: 'block2', name: 'core/heading', attributes: {}, innerBlocks: [] },
- ] );
- } );
-
- it( 'should return an empty set if content is a single unmodified default block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- },
- attributes: {
- block1: {
- modified: false,
- },
- },
- order: {
- '': [ 'block1' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getBlocksForSerialization( state ) ).toEqual( [] );
- } );
-
- it( 'should return a set including a single modified default block', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: {
- clientId: 'block1',
- name: 'core/test-default',
- },
- },
- attributes: {
- block1: {
- modified: true,
- },
- },
- order: {
- '': [ 'block1' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- expect( getBlocksForSerialization( state ) ).toEqual( [
- { clientId: 'block1', name: 'core/test-default', attributes: { modified: true }, innerBlocks: [] },
- ] );
- } );
- } );
-
- describe( 'getEditedPostContent', () => {
- it( 'defers to returning an edited post attribute', () => {
- const block = createBlock( 'core/block' );
-
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ block.clientId ]: omit( block, 'attributes' ),
- },
- attributes: {
- [ block.clientId ]: block.attributes,
- },
- order: {
- '': [ block.clientId ],
- },
- },
- edits: {
- content: 'custom edit',
- },
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( 'custom edit' );
- } );
-
- it( 'returns serialization of blocks', () => {
- const block = createBlock( 'core/block' );
-
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ block.clientId ]: omit( block, 'attributes' ),
- },
- attributes: {
- [ block.clientId ]: block.attributes,
- },
- order: {
- '': [ block.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( '' );
- } );
-
- it( 'returns removep\'d serialization of blocks for single unknown', () => {
- const unknownBlock = createBlock( 'core/test-freeform', {
- content: 'foo
',
- } );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ unknownBlock.clientId ]: omit( unknownBlock, 'attributes' ),
- },
- attributes: {
- [ unknownBlock.clientId ]: unknownBlock.attributes,
- },
- order: {
- '': [ unknownBlock.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( 'foo' );
- } );
-
- it( 'returns non-removep\'d serialization of blocks for multiple unknown', () => {
- const firstUnknown = createBlock( 'core/test-freeform', {
- content: 'foo
',
- } );
- const secondUnknown = createBlock( 'core/test-freeform', {
- content: 'bar
',
- } );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ firstUnknown.clientId ]: omit( firstUnknown, 'attributes' ),
- [ secondUnknown.clientId ]: omit( secondUnknown, 'attributes' ),
- },
- attributes: {
- [ firstUnknown.clientId ]: firstUnknown.attributes,
- [ secondUnknown.clientId ]: secondUnknown.attributes,
- },
- order: {
- '': [ firstUnknown.clientId, secondUnknown.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( 'foo
\n\nbar
' );
- } );
-
- it( 'returns empty string for single unmodified default block', () => {
- const defaultBlock = createBlock( getDefaultBlockName() );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ defaultBlock.clientId ]: omit( defaultBlock, 'attributes' ),
- },
- attributes: {
- [ defaultBlock.clientId ]: defaultBlock.attributes,
- },
- order: {
- '': [ defaultBlock.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( '' );
- } );
-
- it( 'should not return empty string for modified default block', () => {
- const defaultBlock = createBlock( getDefaultBlockName() );
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- [ defaultBlock.clientId ]: {
- ...omit( defaultBlock, 'attributes' ),
- },
- },
- attributes: {
- [ defaultBlock.clientId ]: {
- ...defaultBlock.attributes,
- modified: true,
- },
- },
- order: {
- '': [ defaultBlock.clientId ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- currentPost: {},
- };
-
- const content = getEditedPostContent( state );
-
- expect( content ).toBe( '' );
- } );
- } );
-
- describe( 'canInsertBlockType', () => {
- it( 'should deny blocks that are not registered', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/invalid' ) ).toBe( false );
- } );
-
- it( 'should deny blocks that are not allowed by the editor', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {
- allowedBlockTypes: [],
- },
- };
- expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
- } );
-
- it( 'should allow blocks that are allowed by the editor', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {
- allowedBlockTypes: [ 'core/test-block-a' ],
- },
- };
- expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( true );
- } );
-
- it( 'should deny blocks when the editor has a template lock', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {
- templateLock: 'all',
- },
- };
- expect( canInsertBlockType( state, 'core/test-block-a' ) ).toBe( false );
- } );
-
- it( 'should deny blocks that restrict parent from being inserted into the root', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {},
- attributes: {},
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c' ) ).toBe( false );
- } );
-
- it( 'should deny blocks that restrict parent from being inserted into a restricted parent', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( false );
- } );
-
- it( 'should allow blocks that restrict parent to be inserted into an allowed parent', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-b' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
- } );
-
- it( 'should deny restricted blocks from being inserted into a block that restricts allowedBlocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {
- block1: {
- allowedBlocks: [ 'core/test-block-c' ],
- },
- },
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( false );
- } );
-
- it( 'should allow allowed blocks to be inserted into a block that restricts allowedBlocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {
- block1: {
- allowedBlocks: [ 'core/test-block-b' ],
- },
- },
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-b', 'block1' ) ).toBe( true );
- } );
+ describe( 'getEditedPostContent', () => {
+ let originalDefaultBlockName;
- it( 'should prioritise parent over allowedBlocks', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-b' },
- },
- attributes: {
- block1: {},
- },
- },
- },
- },
- blockListSettings: {
- block1: {
- allowedBlocks: [],
- },
- },
- settings: {},
- };
- expect( canInsertBlockType( state, 'core/test-block-c', 'block1' ) ).toBe( true );
- } );
- } );
+ beforeAll( () => {
+ originalDefaultBlockName = getDefaultBlockName();
- describe( 'getInserterItems', () => {
- it( 'should properly list block type and reusable block items', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- },
- order: {},
- },
- edits: {},
- },
- },
- initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'block1', title: 'Reusable Block 1' },
+ registerBlockType( 'core/default', {
+ category: 'common',
+ title: 'default',
+ attributes: {
+ modified: {
+ type: 'boolean',
+ default: false,
},
},
- currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
- };
- const items = getInserterItems( state );
- const testBlockAItem = items.find( ( item ) => item.id === 'core/test-block-a' );
- expect( testBlockAItem ).toEqual( {
- id: 'core/test-block-a',
- name: 'core/test-block-a',
- initialAttributes: {},
- title: 'Test Block A',
- icon: {
- src: 'test',
- },
- category: 'formatting',
- keywords: [ 'testing' ],
- isDisabled: false,
- utility: 0,
- frecency: 0,
- hasChildBlocksWithInserterSupport: false,
- } );
- const reusableBlockItem = items.find( ( item ) => item.id === 'core/block/1' );
- expect( reusableBlockItem ).toEqual( {
- id: 'core/block/1',
- name: 'core/block',
- initialAttributes: { ref: 1 },
- title: 'Reusable Block 1',
- icon: {
- src: 'test',
- },
- category: 'reusable',
- keywords: [],
- isDisabled: false,
- utility: 0,
- frecency: 0,
+ save: () => null,
} );
+ setDefaultBlockName( 'core/default' );
} );
- it( 'should not list a reusable block item if it is being inserted inside it self', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1ref: {
- name: 'core/block',
- clientId: 'block1ref',
- },
- itselfBlock1: { name: 'core/test-block-a' },
- itselfBlock2: { name: 'core/test-block-b' },
- },
- attributes: {
- block1ref: {
- attributes: {
- ref: 1,
- },
- },
- itselfBlock1: {},
- itselfBlock2: {},
- },
- order: {
- '': [ 'block1ref' ],
- },
- },
- edits: {},
- },
- },
- initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'itselfBlock1', title: 'Reusable Block 1' },
- 2: { clientId: 'itselfBlock2', title: 'Reusable Block 2' },
- },
- },
- currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
- };
- const items = getInserterItems( state, 'itselfBlock1' );
- const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
- expect( reusableBlockItems ).toHaveLength( 1 );
- expect( reusableBlockItems[ 0 ] ).toEqual( {
- id: 'core/block/2',
- name: 'core/block',
- initialAttributes: { ref: 2 },
- title: 'Reusable Block 2',
- icon: {
- src: 'test',
- },
- category: 'reusable',
- keywords: [],
- isDisabled: false,
- utility: 0,
- frecency: 0,
+ afterAll( () => {
+ setDefaultBlockName( originalDefaultBlockName );
+ getBlockTypes().forEach( ( block ) => {
+ unregisterBlockType( block.name );
} );
} );
- it( 'should not list a reusable block item if it is being inserted inside a descendent', () => {
+ it( 'defers to returning an edited post attribute', () => {
+ const block = createBlock( 'core/block' );
+
const state = {
editor: {
present: {
blocks: {
- byClientId: {
- block2ref: {
- name: 'core/block',
- clientId: 'block1ref',
- },
- referredBlock1: { name: 'core/test-block-a' },
- referredBlock2: { name: 'core/test-block-b' },
- childReferredBlock2: { name: 'core/test-block-a' },
- grandchildReferredBlock2: { name: 'core/test-block-b' },
- },
- attributes: {
- block2ref: {
- attributes: {
- ref: 2,
- },
- },
- referredBlock1: {},
- referredBlock2: {},
- childReferredBlock2: {},
- grandchildReferredBlock2: {},
- },
- order: {
- '': [ 'block2ref' ],
- referredBlock2: [ 'childReferredBlock2' ],
- childReferredBlock2: [ 'grandchildReferredBlock2' ],
- },
+ value: [ block ],
},
- edits: {},
- },
- },
- initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'referredBlock1', title: 'Reusable Block 1' },
- 2: { clientId: 'referredBlock2', title: 'Reusable Block 2' },
- },
- },
- currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
- };
- const items = getInserterItems( state, 'grandchildReferredBlock2' );
- const reusableBlockItems = filter( items, [ 'name', 'core/block' ] );
- expect( reusableBlockItems ).toHaveLength( 1 );
- expect( reusableBlockItems[ 0 ] ).toEqual( {
- id: 'core/block/1',
- name: 'core/block',
- initialAttributes: { ref: 1 },
- title: 'Reusable Block 1',
- icon: {
- src: 'test',
- },
- category: 'reusable',
- keywords: [],
- isDisabled: false,
- utility: 0,
- frecency: 0,
- } );
- } );
- it( 'should order items by descending utility and frecency', () => {
- const state = {
- editor: {
- present: {
- blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- block2: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- block2: {},
- },
- order: {},
+ edits: {
+ content: 'custom edit',
},
- edits: {},
},
},
initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'block1', title: 'Reusable Block 1' },
- 2: { clientId: 'block1', title: 'Reusable Block 2' },
- },
- },
currentPost: {},
- preferences: {
- insertUsage: {
- 'core/block/1': { count: 10, time: 1000 },
- 'core/block/2': { count: 20, time: 1000 },
- },
- },
- blockListSettings: {},
- settings: {},
- };
- const itemIDs = getInserterItems( state ).map( ( item ) => item.id );
- expect( itemIDs ).toEqual( [
- 'core/block/2',
- 'core/block/1',
- 'core/test-block-b',
- 'core/test-freeform',
- 'core/test-default',
- 'core/test-block-a',
- ] );
+ };
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( 'custom edit' );
} );
- it( 'should correctly cache the return values', () => {
+ it( 'returns serialization of blocks', () => {
+ const block = createBlock( 'core/block' );
+
const state = {
editor: {
present: {
blocks: {
- byClientId: {
- block1: { name: 'core/test-block-a' },
- block2: { name: 'core/test-block-a' },
- block3: { name: 'core/test-block-a' },
- block4: { name: 'core/test-block-a' },
- },
- attributes: {
- block1: {},
- block2: {},
- block3: {},
- block4: {},
- },
- order: {
- '': [ 'block3', 'block4' ],
- },
+ value: [ block ],
},
edits: {},
},
},
initialEdits: {},
- reusableBlocks: {
- data: {
- 1: { clientId: 'block1', title: 'Reusable Block 1' },
- 2: { clientId: 'block1', title: 'Reusable Block 2' },
- },
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
- };
-
- const stateSecondBlockRestricted = {
- ...state,
- blockListSettings: {
- block4: {
- allowedBlocks: [ 'core/test-block-b' ],
- },
- },
};
- const firstBlockFirstCall = getInserterItems( state, 'block3' );
- const firstBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block3' );
- expect( firstBlockFirstCall ).toBe( firstBlockSecondCall );
- expect( firstBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
- 'core/test-block-b',
- 'core/test-freeform',
- 'core/test-default',
- 'core/test-block-a',
- 'core/block/1',
- 'core/block/2',
- ] );
+ const content = getEditedPostContent( state );
- const secondBlockFirstCall = getInserterItems( state, 'block4' );
- const secondBlockSecondCall = getInserterItems( stateSecondBlockRestricted, 'block4' );
- expect( secondBlockFirstCall.map( ( item ) => item.id ) ).toEqual( [
- 'core/test-block-b',
- 'core/test-freeform',
- 'core/test-default',
- 'core/test-block-a',
- 'core/block/1',
- 'core/block/2',
- ] );
- expect( secondBlockSecondCall.map( ( item ) => item.id ) ).toEqual( [
- 'core/test-block-b',
- ] );
+ expect( content ).toBe( '' );
} );
- it( 'should set isDisabled when a block with `multiple: false` has been used', () => {
+ it( 'returns removep\'d serialization of blocks for single unknown', () => {
+ const unknownBlock = createBlock( 'core/test-freeform', {
+ content: 'foo
',
+ } );
const state = {
editor: {
present: {
blocks: {
- byClientId: {
- block1: { clientId: 'block1', name: 'core/test-block-b' },
- },
- attributes: {
- block1: { attribute: {} },
- },
- order: {
- '': [ 'block1' ],
- },
+ value: [ unknownBlock ],
},
edits: {},
},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state );
- const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
- expect( testBlockBItem.isDisabled ).toBe( true );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( 'foo' );
} );
- it( 'should give common blocks a low utility', () => {
+ it( 'returns non-removep\'d serialization of blocks for multiple unknown', () => {
+ const firstUnknown = createBlock( 'core/test-freeform', {
+ content: 'foo
',
+ } );
+ const secondUnknown = createBlock( 'core/test-freeform', {
+ content: 'bar
',
+ } );
const state = {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [ firstUnknown, secondUnknown ],
},
edits: {},
},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state );
- const testBlockBItem = items.find( ( item ) => item.id === 'core/test-block-b' );
- expect( testBlockBItem.utility ).toBe( INSERTER_UTILITY_LOW );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( 'foo
\n\nbar
' );
} );
- it( 'should give used blocks a medium utility and set a frecency', () => {
+ it( 'returns empty string for single unmodified default block', () => {
+ const defaultBlock = createBlock( getDefaultBlockName() );
const state = {
editor: {
present: {
blocks: {
- byClientId: {},
- attributes: {},
- order: {},
+ value: [ defaultBlock ],
},
edits: {},
},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {
- 'core/test-block-b': { count: 10, time: 1000 },
- },
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state );
- const reusableBlock2Item = items.find( ( item ) => item.id === 'core/test-block-b' );
- expect( reusableBlock2Item.utility ).toBe( INSERTER_UTILITY_MEDIUM );
- expect( reusableBlock2Item.frecency ).toBe( 2.5 );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( '' );
} );
- it( 'should give contextual blocks a high utility', () => {
+ it( 'should not return empty string for modified default block', () => {
+ const defaultBlock = createBlock( getDefaultBlockName() );
const state = {
editor: {
present: {
blocks: {
- byClientId: {
- block1: { name: 'core/test-block-b' },
- },
- attributes: {
- block1: { attribute: {} },
- },
- order: {
- '': [ 'block1' ],
- },
+ value: [ {
+ ...defaultBlock,
+ attributes: {
+ ...defaultBlock.attributes,
+ modified: true,
+ },
+ } ],
},
edits: {},
},
},
initialEdits: {},
- reusableBlocks: {
- data: {},
- },
currentPost: {},
- preferences: {
- insertUsage: {},
- },
- blockListSettings: {},
- settings: {},
};
- const items = getInserterItems( state, 'block1' );
- const testBlockCItem = items.find( ( item ) => item.id === 'core/test-block-c' );
- expect( testBlockCItem.utility ).toBe( INSERTER_UTILITY_HIGH );
+
+ const content = getEditedPostContent( state );
+
+ expect( content ).toBe( '' );
} );
} );
@@ -5307,84 +2549,6 @@ describe( 'selectors', () => {
} );
} );
- describe( 'isValidTemplate', () => {
- it( 'should return true if template is valid', () => {
- const state = {
- template: { isValid: true },
- };
-
- expect( isValidTemplate( state ) ).toBe( true );
- } );
-
- it( 'should return false if template is not valid', () => {
- const state = {
- template: { isValid: false },
- };
-
- expect( isValidTemplate( state ) ).toBe( false );
- } );
- } );
-
- describe( 'getTemplate', () => {
- it( 'should return the template object', () => {
- const template = [];
- const state = {
- settings: { template },
- };
-
- expect( getTemplate( state ) ).toBe( template );
- } );
- } );
-
- describe( 'getTemplateLock', () => {
- it( 'should return the general template lock if no clientId was set', () => {
- const state = {
- settings: { templateLock: 'all' },
- };
-
- expect( getTemplateLock( state ) ).toBe( 'all' );
- } );
-
- it( 'should return null if the specified clientId was not found ', () => {
- const state = {
- settings: { templateLock: 'all' },
- blockListSettings: {
- chicken: {
- templateLock: 'insert',
- },
- },
- };
-
- expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
- } );
-
- it( 'should return null if template lock was not set on the specified block', () => {
- const state = {
- settings: { templateLock: 'all' },
- blockListSettings: {
- chicken: {
- test: 'tes1t',
- },
- },
- };
-
- expect( getTemplateLock( state, 'ribs' ) ).toBe( null );
- } );
-
- it( 'should return the template lock for the specified clientId', () => {
- const state = {
- settings: { templateLock: 'all' },
- blockListSettings: {
- chicken: {
- templateLock: 'insert',
- },
- },
- };
-
- expect( getTemplateLock( state, 'chicken' ) ).toBe( 'insert' );
- } );
- } );
-
describe( 'isPermalinkEditable', () => {
it( 'should be false if there is no permalink', () => {
const state = {
@@ -5536,38 +2700,13 @@ describe( 'selectors', () => {
const state = {
currentPost: {},
editor: {
- present: {},
- },
- };
-
- expect( getPermalinkParts( state ) ).toBeNull();
- } );
- } );
-
- describe( 'getBlockListSettings', () => {
- it( 'should return the settings of a block', () => {
- const state = {
- blockListSettings: {
- chicken: {
- setting1: false,
- },
- ribs: {
- setting2: true,
+ present: {
+ edits: {},
},
},
};
- expect( getBlockListSettings( state, 'chicken' ) ).toEqual( {
- setting1: false,
- } );
- } );
-
- it( 'should return undefined if settings for the block don’t exist', () => {
- const state = {
- blockListSettings: {},
- };
-
- expect( getBlockListSettings( state, 'chicken' ) ).toBe( undefined );
+ expect( getPermalinkParts( state ) ).toBeNull();
} );
} );
From 61a418c44ee9beacf884f51330934f9b09788b09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ella=20Van=C2=A0Durpe?=
Date: Fri, 22 Feb 2019 10:08:02 +0100
Subject: [PATCH 013/169] RichText: warn when using inline container (#13921)
* RichText: warn when using inline container
* Add env check
* Update documentation
---
.../editor/src/components/rich-text/README.md | 2 +-
.../editor/src/components/rich-text/index.js | 17 +++++++++++++++--
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/packages/editor/src/components/rich-text/README.md b/packages/editor/src/components/rich-text/README.md
index 6774ca3879f49..b3565b729cca3 100644
--- a/packages/editor/src/components/rich-text/README.md
+++ b/packages/editor/src/components/rich-text/README.md
@@ -14,7 +14,7 @@ Render a rich [`contenteditable` input](https://developer.mozilla.org/en-US/docs
### `tagName: String`
-*Default: `div`.* The [tag name](https://www.w3.org/TR/html51/syntax.html#tag-name) of the editable element.
+*Default: `div`.* The [tag name](https://www.w3.org/TR/html51/syntax.html#tag-name) of the editable element. Elements that display inline are not supported.
### `placeholder: String`
diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js
index 30cc785d63448..d086cf24795fb 100644
--- a/packages/editor/src/components/rich-text/index.js
+++ b/packages/editor/src/components/rich-text/index.js
@@ -67,7 +67,7 @@ import { RemoveBrowserShortcuts } from './remove-browser-shortcuts';
* Browser dependencies
*/
-const { getSelection } = window;
+const { getSelection, getComputedStyle } = window;
/**
* All inserting input types that would insert HTML into the DOM.
@@ -151,7 +151,20 @@ export class RichText extends Component {
}
setRef( node ) {
- this.editableRef = node;
+ if ( node ) {
+ if ( process.env.NODE_ENV === 'development' ) {
+ const computedStyle = getComputedStyle( node );
+
+ if ( computedStyle.display === 'inline' ) {
+ // eslint-disable-next-line no-console
+ console.warn( 'RichText cannot be used with an inline container. Please use a different tagName.' );
+ }
+ }
+
+ this.editableRef = node;
+ } else {
+ delete this.editableRef;
+ }
}
setFocusedElement() {
From d9b232d1d15db418d22db89c53626542a8efd654 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Fri, 22 Feb 2019 12:07:51 +0100
Subject: [PATCH 014/169] Try updating the WP database (#14048)
---
bin/install-wordpress.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/bin/install-wordpress.sh b/bin/install-wordpress.sh
index a5ba87ec4f4d5..92cee6f514e90 100755
--- a/bin/install-wordpress.sh
+++ b/bin/install-wordpress.sh
@@ -78,6 +78,8 @@ if [ "$WP_VERSION" == "latest" ]; then
# Check for WordPress updates, to make sure we're running the very latest version.
echo -e $(status_message "Updating WordPress to the latest version...")
docker-compose $DOCKER_COMPOSE_FILE_OPTIONS run --rm -u 33 $CLI core update --quiet
+ echo -e $(status_message "Updating The WordPress Database...")
+ docker-compose $DOCKER_COMPOSE_FILE_OPTIONS run --rm -u 33 $CLI core update-db --quiet
fi
# If the 'wordpress' volume wasn't during the down/up earlier, but the post port has changed, we need to update it.
From 666989fd1cbfb81466d575681e282beb175e0ab2 Mon Sep 17 00:00:00 2001
From: Stefanos Togoulidis
Date: Fri, 22 Feb 2019 20:10:20 +0200
Subject: [PATCH 015/169] Merge native mobile release v1.0 to master (#14061)
* Bump plugin version to 5.1.0-rc.1
* RichText: only ignore input types that insert HTML (#13914)
* RichText: only ignore input types that insert HTML
* Mark RichTextInputEvent as unstable
* Use Set
* Bump plugin version to 5.1.0
* Deprecate RichTextInputEvent on mobile too (#13975)
* The undelying RichText component implementation has changed the parameters returned onChange, and we forgot to update the PostTitle component (#13967)
* Fixes wrong state comparison (#13987)
Upload media progress bar is missing while media is uploading new
* Re-add rootTagsToEliminate prop (#14006)
* [Mobile]Update PostTitle to apply borders when it is focused (#13970)
* Trigger onFocusStatusChange from PostTitle
* Fix lint issue
* Update post title shadow mechanism
Also open inner ref so that focus state can be updated when focus is made programmatically
* Update props
* Update onRef as ref
* Update title padding&margin
* Mobile: Rename ref to innerRef on PostTitle (#14024)
* Fixes a red screen in mobile. (#14011)
* Change background color on image placeholder block (#14033)
* Changed upload media icon color
* Changed media placeholder background color
* Fix post title native syntax (#14041)
* Fix unexpected token in native code
* Dummy commit to trigger Travis
* Include the rnmobile release branch to Travis builds
* Mobile: Links UI using BottomSheet component (#13972)
* Mobile: Replaced Links UI with bottom-sheet component
* Mobile links UI: Removed commented code.
* Mobile: Fix lint issues
* Mobile Links UI: Remove autofocus on Android.
This hides an issue where the modal sometimes will be under the keyboard on Android.
* Fixes pasting links. (#14038)
* Update post title vertical paddings (#14040)
* Add try/catch fallback to plain text for pasteHandler (#14044)
* Fix link interface. (#14052)
* [Mobile]Fix title padding on Android (#14057)
* Remove title vertical paddings for Android
* Revert "Remove title vertical paddings for Android"
This reverts commit 09f0d3592f77509e6a19e7712bba725a6118ab0f.
* Import padding variables
* Revert wrong format image color (#14058)
* Stop building the mobile release branch on Travis (#14060)
---
.../block-library/src/image/edit.native.js | 2 +-
.../editor/src/components/index.native.js | 2 +-
.../media-placeholder/styles.native.scss | 2 +-
.../mobile/bottom-sheet/cell.native.js | 13 +-
.../mobile/bottom-sheet/styles.native.scss | 1 +
.../src/components/post-title/index.native.js | 76 ++++++++---
.../components/post-title/style.native.scss | 10 ++
.../src/components/rich-text/index.native.js | 34 +++--
.../rich-text/input-event.native.js | 2 +-
.../format-library/src/link/button.native.js | 24 ----
.../format-library/src/link/index.native.js | 20 ++-
.../format-library/src/link/modal.native.js | 129 ++++++++----------
.../format-library/src/link/modal.native.scss | 81 +----------
13 files changed, 174 insertions(+), 222 deletions(-)
create mode 100644 packages/editor/src/components/post-title/style.native.scss
delete mode 100644 packages/format-library/src/link/button.native.js
diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js
index 73b3eba1ab5fa..eabcdc9941a26 100644
--- a/packages/block-library/src/image/edit.native.js
+++ b/packages/block-library/src/image/edit.native.js
@@ -124,7 +124,7 @@ class ImageEdit extends React.Component {
updateMediaProgress( payload ) {
const { setAttributes } = this.props;
this.setState( { progress: payload.progress, isUploadInProgress: true, isUploadFailed: false } );
- if ( payload.mediaUrl !== undefined ) {
+ if ( payload.mediaUrl ) {
setAttributes( { url: payload.mediaUrl } );
}
}
diff --git a/packages/editor/src/components/index.native.js b/packages/editor/src/components/index.native.js
index cd974fd2dfea1..d7d35e3873e2a 100644
--- a/packages/editor/src/components/index.native.js
+++ b/packages/editor/src/components/index.native.js
@@ -5,7 +5,7 @@ export {
default as RichText,
RichTextShortcut,
RichTextToolbarButton,
- RichTextInputEvent,
+ UnstableRichTextInputEvent,
} from './rich-text';
export { default as MediaPlaceholder } from './media-placeholder';
export { default as BlockFormatControls } from './block-format-controls';
diff --git a/packages/editor/src/components/media-placeholder/styles.native.scss b/packages/editor/src/components/media-placeholder/styles.native.scss
index 964bb4371310d..d3001491eb73a 100644
--- a/packages/editor/src/components/media-placeholder/styles.native.scss
+++ b/packages/editor/src/components/media-placeholder/styles.native.scss
@@ -4,7 +4,7 @@
flex-direction: column;
align-items: center;
justify-content: center;
- background-color: #f2f2f2;
+ background-color: #e9eff3;
padding-left: 12;
padding-right: 12;
padding-top: 12;
diff --git a/packages/editor/src/components/mobile/bottom-sheet/cell.native.js b/packages/editor/src/components/mobile/bottom-sheet/cell.native.js
index 6ac26d2d00ff8..9acc94bbfb3bb 100644
--- a/packages/editor/src/components/mobile/bottom-sheet/cell.native.js
+++ b/packages/editor/src/components/mobile/bottom-sheet/cell.native.js
@@ -16,10 +16,10 @@ import styles from './styles.scss';
import platformStyles from './cellStyles.scss';
export default class Cell extends Component {
- constructor() {
+ constructor( props ) {
super( ...arguments );
this.state = {
- isEditingValue: false,
+ isEditingValue: props.autoFocus || false,
};
}
@@ -53,7 +53,7 @@ export default class Cell extends Component {
const onCellPress = () => {
if ( isValueEditable ) {
- this.setState( { isEditingValue: true } );
+ startEditing();
} else if ( onPress !== undefined ) {
onPress();
}
@@ -63,6 +63,12 @@ export default class Cell extends Component {
this.setState( { isEditingValue: false } );
};
+ const startEditing = () => {
+ if ( this.state.isEditingValue === false ) {
+ this.setState( { isEditingValue: true } );
+ }
+ };
+
const separatorStyle = () => {
const leftMarginStyle = { ...styles.cellSeparator, ...platformStyles.separatorMarginLeft };
switch ( separatorType ) {
@@ -97,6 +103,7 @@ export default class Cell extends Component {
onChangeText={ onChangeValue }
editable={ isValueEditable }
pointerEvents={ this.state.isEditingValue ? 'auto' : 'none' }
+ onFocus={ startEditing }
onBlur={ finishEditing }
{ ...valueProps }
/>
diff --git a/packages/editor/src/components/mobile/bottom-sheet/styles.native.scss b/packages/editor/src/components/mobile/bottom-sheet/styles.native.scss
index 198d526092a85..53764ee4fe38f 100644
--- a/packages/editor/src/components/mobile/bottom-sheet/styles.native.scss
+++ b/packages/editor/src/components/mobile/bottom-sheet/styles.native.scss
@@ -31,6 +31,7 @@
border-top-left-radius: 8px;
width: 100%;
max-width: 512;
+ padding-bottom: 0;
}
.content {
diff --git a/packages/editor/src/components/post-title/index.native.js b/packages/editor/src/components/post-title/index.native.js
index 8c79b43414a1a..8204220b3be6d 100644
--- a/packages/editor/src/components/post-title/index.native.js
+++ b/packages/editor/src/components/post-title/index.native.js
@@ -1,3 +1,8 @@
+/**
+ * External dependencies
+ */
+import { View } from 'react-native';
+
/**
* WordPress dependencies
*/
@@ -8,6 +13,11 @@ import { withDispatch } from '@wordpress/data';
import { withFocusOutside } from '@wordpress/components';
import { withInstanceId, compose } from '@wordpress/compose';
+/**
+ * Internal dependencies
+ */
+import styles from './style.scss';
+
const minHeight = 30;
class PostTitle extends Component {
@@ -16,6 +26,7 @@ class PostTitle extends Component {
this.onSelect = this.onSelect.bind( this );
this.onUnselect = this.onUnselect.bind( this );
+ this.titleViewRef = null;
this.state = {
isSelected: false,
@@ -23,10 +34,23 @@ class PostTitle extends Component {
};
}
+ componentDidMount() {
+ if ( this.props.innerRef ) {
+ this.props.innerRef( this );
+ }
+ }
+
handleFocusOutside() {
this.onUnselect();
}
+ focus() {
+ if ( this.titleViewRef ) {
+ this.titleViewRef.focus();
+ this.setState( { isSelected: true } );
+ }
+ }
+
onSelect() {
this.setState( { isSelected: true } );
this.props.clearSelectedBlock();
@@ -41,33 +65,41 @@ class PostTitle extends Component {
placeholder,
style,
title,
+ focusedBorderColor,
+ borderStyle,
} = this.props;
const decodedPlaceholder = decodeEntities( placeholder );
+ const borderColor = this.state.isSelected ? focusedBorderColor : 'transparent';
return (
- {
- this.props.onUpdate( event.content );
- } }
- onContentSizeChange={ ( event ) => {
- this.setState( { aztecHeight: event.aztecHeight } );
- } }
- placeholder={ decodedPlaceholder }
- value={ title }
- onSplit={ this.props.onEnterPress }
- setRef={ this.props.setRef }
- >
-
+
+ {
+ this.props.onUpdate( value );
+ } }
+ onContentSizeChange={ ( event ) => {
+ this.setState( { aztecHeight: event.aztecHeight } );
+ } }
+ placeholder={ decodedPlaceholder }
+ value={ title }
+ onSplit={ this.props.onEnterPress }
+ setRef={ ( ref ) => {
+ this.titleViewRef = ref;
+ } }
+ >
+
+
);
}
}
diff --git a/packages/editor/src/components/post-title/style.native.scss b/packages/editor/src/components/post-title/style.native.scss
new file mode 100644
index 0000000000000..b5d36037d9659
--- /dev/null
+++ b/packages/editor/src/components/post-title/style.native.scss
@@ -0,0 +1,10 @@
+
+@import "variables.scss";
+
+.titleContainer {
+ padding-left: 16;
+ padding-right: 16;
+ padding-top: $title-block-padding-top;
+ padding-bottom: $title-block-padding-bottom;
+ margin-top: 24;
+}
diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js
index e7deab608e266..1e3de4f168086 100644
--- a/packages/editor/src/components/rich-text/index.native.js
+++ b/packages/editor/src/components/rich-text/index.native.js
@@ -42,6 +42,27 @@ const unescapeSpaces = ( text ) => {
return text.replace( / | /gi, ' ' );
};
+/**
+ * Calls {@link pasteHandler} with a fallback to plain text when HTML processing
+ * results in errors
+ *
+ * @param {Object} [options] The options to pass to {@link pasteHandler}
+ *
+ * @return {Array|string} A list of blocks or a string, depending on
+ * `handlerMode`.
+ */
+const saferPasteHandler = ( options ) => {
+ try {
+ return pasteHandler( options );
+ } catch ( error ) {
+ window.console.log( 'Pasting HTML failed:', error );
+ window.console.log( 'HTML:', options.HTML );
+ window.console.log( 'Falling back to plain text.' );
+ // fallback to plain text
+ return pasteHandler( { ...options, HTML: '' } );
+ }
+};
+
const gutenbergFormatNamesToAztec = {
'core/bold': 'bold',
'core/italic': 'italic',
@@ -289,9 +310,8 @@ export class RichText extends Component {
},
} );
this.lastContent = this.valueToFormat( linkedRecord );
- this.props.onChange( {
- content: this.lastContent,
- } );
+ this.lastEventCount = undefined;
+ this.props.onChange( this.lastContent );
// Allows us to ask for this information when we get a report.
window.console.log( 'Created link:\n\n', trimmedText );
@@ -310,7 +330,7 @@ export class RichText extends Component {
mode = 'AUTO';
}
- const pastedContent = pasteHandler( {
+ const pastedContent = saferPasteHandler( {
HTML: pastedHtml,
plainText: pastedText,
mode,
@@ -324,9 +344,7 @@ export class RichText extends Component {
const newContent = this.valueToFormat( insertedContent );
this.lastEventCount = undefined;
this.lastContent = newContent;
- this.props.onChange( {
- content: this.lastContent,
- } );
+ this.props.onChange( this.lastContent );
} else if ( onSplit ) {
if ( ! pastedContent.length ) {
return;
@@ -568,4 +586,4 @@ RichTextContainer.Content.defaultProps = {
export default RichTextContainer;
export { RichTextShortcut } from './shortcut';
export { RichTextToolbarButton } from './toolbar-button';
-export { RichTextInputEvent } from './input-event';
+export { UnstableRichTextInputEvent } from './input-event';
diff --git a/packages/editor/src/components/rich-text/input-event.native.js b/packages/editor/src/components/rich-text/input-event.native.js
index 71f2ce4797e24..b8f0fad0a969c 100644
--- a/packages/editor/src/components/rich-text/input-event.native.js
+++ b/packages/editor/src/components/rich-text/input-event.native.js
@@ -3,7 +3,7 @@
*/
import { Component } from '@wordpress/element';
-export class RichTextInputEvent extends Component {
+export class UnstableRichTextInputEvent extends Component {
render() {
return null;
}
diff --git a/packages/format-library/src/link/button.native.js b/packages/format-library/src/link/button.native.js
deleted file mode 100644
index fa8fd004c5a37..0000000000000
--- a/packages/format-library/src/link/button.native.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * External dependencies
- */
-import { TouchableOpacity, View } from 'react-native';
-
-export default function Button( props ) {
- const {
- children,
- onClick,
- disabled,
- } = props;
-
- return (
-
-
- { children }
-
-
- );
-}
diff --git a/packages/format-library/src/link/index.native.js b/packages/format-library/src/link/index.native.js
index 4a2def1614323..4ea3ee0e30934 100644
--- a/packages/format-library/src/link/index.native.js
+++ b/packages/format-library/src/link/index.native.js
@@ -107,17 +107,15 @@ export const link = {
return (
- { this.state.addingLink &&
-
- }
+
-
-
-
-
-
- { __( 'Remove' ) }
-
-
-
- { __( 'Link Settings' ) }
-
-
-
- { __( 'Done' ) }
-
-
-
-
-
-
- { __( 'URL' ) }
-
-
-
-
-
-
- { __( 'Link Text' ) }
-
-
-
-
-
-
- { __( 'Open in a new window' ) }
-
-
-
-
-
-
-
+ { /* eslint-disable jsx-a11y/no-autofocus */
+
+ /* eslint-enable jsx-a11y/no-autofocus */ }
+
+
+
+
+
+
);
}
}
diff --git a/packages/format-library/src/link/modal.native.scss b/packages/format-library/src/link/modal.native.scss
index 72f6a647b2396..e6a590eab85f7 100644
--- a/packages/format-library/src/link/modal.native.scss
+++ b/packages/format-library/src/link/modal.native.scss
@@ -1,80 +1,3 @@
-
-.bottomModal {
- justify-content: flex-end;
- margin: 0;
+.clearLinkButton {
+ color: $alert-red;
}
-
-.dragIndicator {
- background-color: $light-gray-400;
- height: 4px;
- width: 10%;
- top: -12px;
- margin: auto;
- border-radius: 2px;
-}
-
-.separator {
- background-color: $light-gray-400;
- height: 1px;
- width: 95%;
- margin: auto;
-}
-
-.content {
- background-color: $white;
- padding: 18px 10px 5px 10px;
- justify-content: center;
- border-top-right-radius: 8px;
- border-top-left-radius: 8px;
-}
-
-.head {
- flex-direction: row;
- width: 100%;
- margin-bottom: 5px;
- justify-content: space-between;
- align-items: center;
- align-content: center;
-}
-
-.title {
- color: $dark-gray-600;
- font-size: 18px;
- font-weight: 600;
- flex: 1;
- text-align: center;
-}
-
-.buttonText {
- font-size: 18px;
- padding: 5px;
-}
-
-.inlineInput {
- flex-direction: row;
- width: 100%;
- justify-content: space-between;
- align-items: center;
- margin: 5px 0;
-}
-
-.inlineInputLabel {
- padding: 10px 10px;
- color: $dark-gray-600;
- font-size: 14px;
- font-weight: bold;
-}
-
-.inlineInputValue {
- flex-grow: 1;
- font-size: 14px;
- text-align: right;
- align-items: stretch;
- align-self: flex-end;
- padding: 10px;
-}
-
-.inlineInputValueSwitch {
- padding: 5px;
-}
-
From 2d39089798f9d941858c7b1c6d2c65b660272959 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?=
Date: Fri, 22 Feb 2019 20:01:05 +0100
Subject: [PATCH 016/169] Avoid mutating imported default config in webpack
config (#14039)
---
webpack.config.js | 150 +++++++++++++++++++++++-----------------------
1 file changed, 75 insertions(+), 75 deletions(-)
diff --git a/webpack.config.js b/webpack.config.js
index a5dd63c3bd982..1550b0712ab0d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -13,7 +13,7 @@ const { basename } = require( 'path' );
*/
const CustomTemplatedPathPlugin = require( '@wordpress/custom-templated-path-webpack-plugin' );
const LibraryExportDefaultPlugin = require( '@wordpress/library-export-default-webpack-plugin' );
-const config = require( '@wordpress/scripts/config/webpack.config' );
+const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
const { camelCaseDash } = require( '@wordpress/scripts/utils' );
/**
@@ -27,82 +27,82 @@ const gutenbergPackages = Object.keys( dependencies )
.filter( ( packageName ) => packageName.startsWith( WORDPRESS_NAMESPACE ) )
.map( ( packageName ) => packageName.replace( WORDPRESS_NAMESPACE, '' ) );
-config.entry = gutenbergPackages.reduce( ( memo, packageName ) => {
- const name = camelCaseDash( packageName );
- memo[ name ] = `./packages/${ packageName }`;
- return memo;
-}, {} );
+module.exports = {
+ ...defaultConfig,
+ entry: gutenbergPackages.reduce( ( memo, packageName ) => {
+ const name = camelCaseDash( packageName );
+ memo[ name ] = `./packages/${ packageName }`;
+ return memo;
+ }, {} ),
+ output: {
+ filename: './build/[basename]/index.js',
+ path: __dirname,
+ library: [ 'wp', '[name]' ],
+ libraryTarget: 'this',
+ },
+ plugins: [
+ ...defaultConfig.plugins,
+ new DefinePlugin( {
+ // Inject the `GUTENBERG_PHASE` global, used for feature flagging.
+ // eslint-disable-next-line @wordpress/gutenberg-phase
+ 'process.env.GUTENBERG_PHASE': JSON.stringify( parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ) || 1 ),
+ } ),
+ // Create RTL files with a -rtl suffix
+ new WebpackRTLPlugin( {
+ suffix: '-rtl',
+ minify: defaultConfig.mode === 'production' ? { safe: true } : false,
+ } ),
+ new CustomTemplatedPathPlugin( {
+ basename( path, data ) {
+ let rawRequest;
-config.output = {
- filename: './build/[basename]/index.js',
- path: __dirname,
- library: [ 'wp', '[name]' ],
- libraryTarget: 'this',
-};
-
-config.plugins.push(
- new DefinePlugin( {
- // Inject the `GUTENBERG_PHASE` global, used for feature flagging.
- // eslint-disable-next-line @wordpress/gutenberg-phase
- 'process.env.GUTENBERG_PHASE': JSON.stringify( parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ) || 1 ),
- } ),
- // Create RTL files with a -rtl suffix
- new WebpackRTLPlugin( {
- suffix: '-rtl',
- minify: config.mode === 'production' ? { safe: true } : false,
- } ),
- new CustomTemplatedPathPlugin( {
- basename( path, data ) {
- let rawRequest;
-
- const entryModule = get( data, [ 'chunk', 'entryModule' ], {} );
- switch ( entryModule.type ) {
- case 'javascript/auto':
- rawRequest = entryModule.rawRequest;
- break;
-
- case 'javascript/esm':
- rawRequest = entryModule.rootModule.rawRequest;
- break;
- }
+ const entryModule = get( data, [ 'chunk', 'entryModule' ], {} );
+ switch ( entryModule.type ) {
+ case 'javascript/auto':
+ rawRequest = entryModule.rawRequest;
+ break;
- if ( rawRequest ) {
- return basename( rawRequest );
- }
+ case 'javascript/esm':
+ rawRequest = entryModule.rootModule.rawRequest;
+ break;
+ }
- return path;
- },
- } ),
- new LibraryExportDefaultPlugin( [
- 'api-fetch',
- 'deprecated',
- 'dom-ready',
- 'redux-routine',
- 'token-list',
- ].map( camelCaseDash ) ),
- new CopyWebpackPlugin(
- gutenbergPackages.map( ( packageName ) => ( {
- from: `./packages/${ packageName }/build-style/*.css`,
- to: `./build/${ packageName }/`,
- flatten: true,
- transform: ( content ) => {
- if ( config.mode === 'production' ) {
- return postcss( [
- require( 'cssnano' )( {
- preset: [ 'default', {
- discardComments: {
- removeAll: true,
- },
- } ],
- } ),
- ] )
- .process( content, { from: 'src/app.css', to: 'dest/app.css' } )
- .then( ( result ) => result.css );
+ if ( rawRequest ) {
+ return basename( rawRequest );
}
- return content;
- },
- } ) )
- )
-);
-module.exports = config;
+ return path;
+ },
+ } ),
+ new LibraryExportDefaultPlugin( [
+ 'api-fetch',
+ 'deprecated',
+ 'dom-ready',
+ 'redux-routine',
+ 'token-list',
+ ].map( camelCaseDash ) ),
+ new CopyWebpackPlugin(
+ gutenbergPackages.map( ( packageName ) => ( {
+ from: `./packages/${ packageName }/build-style/*.css`,
+ to: `./build/${ packageName }/`,
+ flatten: true,
+ transform: ( content ) => {
+ if ( defaultConfig.mode === 'production' ) {
+ return postcss( [
+ require( 'cssnano' )( {
+ preset: [ 'default', {
+ discardComments: {
+ removeAll: true,
+ },
+ } ],
+ } ),
+ ] )
+ .process( content, { from: 'src/app.css', to: 'dest/app.css' } )
+ .then( ( result ) => result.css );
+ }
+ return content;
+ },
+ } ) )
+ ),
+ ],
+};
From 4fa15b5fd4117dea45dde052fa9d5c14798e5ae1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?=
Date: Fri, 22 Feb 2019 20:44:38 +0100
Subject: [PATCH 017/169] Make Babel import JSX pragma plugin aware of
`wp.element.createElement` (#13809)
* Test: Add test which verifies wheter JSX pragma detects WP global
* Update packages/babel-plugin-import-jsx-pragma/test/index.js
Co-Authored-By: gziolo
* Skip import when the scope variable is already defined
* Add failing tests for inner scope variable defined verification
* Add import statement when there is any undefined scope variable
* Docs: Add details about changes introduced to Babel plugin
---
.../CHANGELOG.md | 6 +++
.../babel-plugin-import-jsx-pragma/README.md | 2 +-
.../src/index.js | 9 ++++-
.../test/index.js | 40 ++++++++++++++++++-
4 files changed, 53 insertions(+), 4 deletions(-)
diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
index 33b89b5ae57b0..ffa68f4404f0d 100644
--- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
+++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 2.0.0 (Unreleased)
+
+### Breaking Change
+
+- Plugin skips now adding import JSX pragma when the scope variable is defined for all JSX elements ([#13809](https://github.com/WordPress/gutenberg/pull/13809)).
+
## 1.1.0 (2018-09-05)
### New Feature
diff --git a/packages/babel-plugin-import-jsx-pragma/README.md b/packages/babel-plugin-import-jsx-pragma/README.md
index fc32f27a90204..85d7e69655455 100644
--- a/packages/babel-plugin-import-jsx-pragma/README.md
+++ b/packages/babel-plugin-import-jsx-pragma/README.md
@@ -4,7 +4,7 @@ Babel transform plugin for automatically injecting an import to be used as the p
[JSX](https://reactjs.org/docs/jsx-in-depth.html) is merely a syntactic sugar for a function call, typically to `React.createElement` when used with [React](https://reactjs.org/). As such, it requires that the function referenced by this transform be within the scope of the file where the JSX occurs. In a typical React project, this means React must be imported in any file where JSX exists.
-**Babel Plugin Import JSX Pragma** automates this process by introducing the necessary import automatically wherever JSX exists, allowing you to use JSX in your code without thinking to ensure the transformed function is within scope.
+**Babel Plugin Import JSX Pragma** automates this process by introducing the necessary import automatically wherever JSX exists, allowing you to use JSX in your code without thinking to ensure the transformed function is within scope. It respects existing import statements, as well as scope variable declarations.
## Installation
diff --git a/packages/babel-plugin-import-jsx-pragma/src/index.js b/packages/babel-plugin-import-jsx-pragma/src/index.js
index 89963e67d27e8..68e94e1ffc37d 100644
--- a/packages/babel-plugin-import-jsx-pragma/src/index.js
+++ b/packages/babel-plugin-import-jsx-pragma/src/index.js
@@ -44,6 +44,13 @@ export default function( babel ) {
visitor: {
JSXElement( path, state ) {
state.hasJSX = true;
+ if ( state.hasUndeclaredScopeVariable ) {
+ return;
+ }
+
+ const { scopeVariable } = getOptions( state );
+
+ state.hasUndeclaredScopeVariable = ! path.scope.hasBinding( scopeVariable );
},
ImportDeclaration( path, state ) {
if ( state.hasImportedScopeVariable ) {
@@ -71,7 +78,7 @@ export default function( babel ) {
},
Program: {
exit( path, state ) {
- if ( ! state.hasJSX || state.hasImportedScopeVariable ) {
+ if ( ! state.hasJSX || state.hasImportedScopeVariable || ! state.hasUndeclaredScopeVariable ) {
return;
}
diff --git a/packages/babel-plugin-import-jsx-pragma/test/index.js b/packages/babel-plugin-import-jsx-pragma/test/index.js
index 800b75e9727d6..d70cf2313c540 100644
--- a/packages/babel-plugin-import-jsx-pragma/test/index.js
+++ b/packages/babel-plugin-import-jsx-pragma/test/index.js
@@ -35,11 +35,18 @@ describe( 'babel-plugin-import-jsx-pragma', () => {
expect( string ).toBe( original );
} );
+ it( 'does nothing if the scope variable is already defined', () => {
+ const original = 'const React = require("react");\n\nlet foo = ;';
+ const string = getTransformedCode( original );
+
+ expect( string ).toBe( original );
+ } );
+
it( 'adds import for scope variable', () => {
const original = 'let foo = ;';
const string = getTransformedCode( original );
- expect( string ).toBe( 'import React from "react";\nlet foo = ;' );
+ expect( string ).toBe( 'import React from "react";\n' + original );
} );
it( 'allows options customization', () => {
@@ -50,6 +57,35 @@ describe( 'babel-plugin-import-jsx-pragma', () => {
isDefault: false,
} );
- expect( string ).toBe( 'import { createElement } from "@wordpress/element";\nlet foo = ;' );
+ expect( string ).toBe( 'import { createElement } from "@wordpress/element";\n' + original );
+ } );
+
+ it( 'adds import for scope variable even when defined inside the local scope', () => {
+ const original = 'let foo = ;\n\nfunction local() {\n const createElement = wp.element.createElement;\n}';
+ const string = getTransformedCode( original, {
+ scopeVariable: 'createElement',
+ source: '@wordpress/element',
+ isDefault: false,
+ } );
+
+ expect( string ).toBe( 'import { createElement } from "@wordpress/element";\n' + original );
+ } );
+
+ it( 'does nothing if the outer scope variable is already defined when using custom options', () => {
+ const original = 'const {\n createElement\n} = wp.element;\nlet foo = ;';
+ const string = getTransformedCode( original, {
+ scopeVariable: 'createElement',
+ } );
+
+ expect( string ).toBe( original );
+ } );
+
+ it( 'does nothing if the inner scope variable is already defined when using custom options', () => {
+ const original = '(function () {\n const {\n createElement\n } = wp.element;\n let foo = ;\n})();';
+ const string = getTransformedCode( original, {
+ scopeVariable: 'createElement',
+ } );
+
+ expect( string ).toBe( original );
} );
} );
From 3feaa48e4bdb18a5368bd6322bb91a3b873b251f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?=
Date: Sun, 24 Feb 2019 16:24:45 +0100
Subject: [PATCH 018/169] Upgrade Jest to version 24 (breaking changes)
(#13922)
* Upgrade Jest to version 24 (breaking changes)
* Update changelog files to list all dependencies upgraded
* Downgrade puppeteer to the previous version 1.6.1
* Try to fix failing e2e tests setup
* Added clarification in the changelog
* Testing: Remove expect-puppeteer import reference
* Address issues raised during code review
---
package-lock.json | 4060 ++++++++---------
package.json | 2 +-
packages/babel-preset-default/CHANGELOG.md | 6 +
packages/babel-preset-default/package.json | 3 +-
packages/babel-preset-default/test/index.js | 2 +-
.../components/src/slot-fill/test/index.js | 4 +-
packages/e2e-test-utils/package.json | 2 +-
.../e2e-tests/config/setup-test-framework.js | 1 -
packages/e2e-tests/jest.config.js | 9 +-
packages/e2e-tests/package.json | 3 +-
.../src/components/block-edit/test/edit.js | 7 +-
.../test/__snapshots__/index.js.snap | 7 +-
.../components/page-attributes/test/check.js | 6 +-
packages/jest-console/CHANGELOG.md | 8 +-
packages/jest-console/README.md | 12 +-
packages/jest-console/package.json | 4 +-
packages/jest-preset-default/CHANGELOG.md | 11 +
packages/jest-preset-default/README.md | 16 +-
packages/jest-preset-default/jest-preset.json | 9 +-
packages/jest-preset-default/package.json | 10 +-
.../scripts/setup-test-framework.js | 3 -
packages/jest-puppeteer-axe/README.md | 12 +-
packages/jest-puppeteer-axe/package.json | 4 +-
packages/scripts/CHANGELOG.md | 2 +
packages/scripts/config/jest-e2e.config.js | 2 +-
packages/scripts/config/jest-unit.config.js | 2 +-
packages/scripts/package.json | 4 +-
27 files changed, 2048 insertions(+), 2163 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index a5737be8fefbc..4c9b3ccb412e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2521,8 +2521,7 @@
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@babel/runtime": "^7.3.1",
- "@wordpress/browserslist-config": "file:packages/browserslist-config",
- "babel-core": "^7.0.0-bridge.0"
+ "@wordpress/browserslist-config": "file:packages/browserslist-config"
}
},
"@wordpress/blob": {
@@ -2733,7 +2732,6 @@
"@wordpress/e2e-test-utils": "file:packages/e2e-test-utils",
"@wordpress/jest-console": "file:packages/jest-console",
"@wordpress/scripts": "file:packages/scripts",
- "expect-puppeteer": "^3.2.0",
"lodash": "^4.17.11"
}
},
@@ -2886,7 +2884,7 @@
"dev": true,
"requires": {
"@babel/runtime": "^7.3.1",
- "jest-matcher-utils": "^23.6.0",
+ "jest-matcher-utils": "^24.0.0",
"lodash": "^4.17.11"
}
},
@@ -2895,17 +2893,17 @@
"dev": true,
"requires": {
"@wordpress/jest-console": "file:packages/jest-console",
- "babel-jest": "^23.6.0",
- "enzyme": "^3.7.0",
- "enzyme-adapter-react-16": "^1.6.0",
- "jest-enzyme": "^6.0.2"
+ "babel-jest": "^24.1.0",
+ "enzyme": "^3.9.0",
+ "enzyme-adapter-react-16": "^1.9.1",
+ "enzyme-to-json": "^3.3.5"
}
},
"@wordpress/jest-puppeteer-axe": {
"version": "file:packages/jest-puppeteer-axe",
"dev": true,
"requires": {
- "axe-puppeteer": "^0.1.0"
+ "axe-puppeteer": "^1.0.0"
}
},
"@wordpress/keycodes": {
@@ -3021,8 +3019,8 @@
"check-node-version": "^3.1.1",
"cross-spawn": "^5.1.0",
"eslint": "^5.12.1",
- "jest": "^23.6.0",
- "jest-puppeteer": "3.2.1",
+ "jest": "^24.1.0",
+ "jest-puppeteer": "^4.0.0",
"npm-package-json-lint": "^3.3.1",
"puppeteer": "1.6.1",
"read-pkg-up": "^1.0.1",
@@ -3260,12 +3258,12 @@
}
},
"append-transform": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz",
- "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
+ "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
"dev": true,
"requires": {
- "default-require-extensions": "^1.0.0"
+ "default-require-extensions": "^2.0.0"
}
},
"aproba": {
@@ -3331,6 +3329,12 @@
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
"dev": true
},
+ "array-filter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
+ "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=",
+ "dev": true
+ },
"array-find-index": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
@@ -3574,79 +3578,14 @@
"dev": true
},
"axe-puppeteer": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/axe-puppeteer/-/axe-puppeteer-0.1.0.tgz",
- "integrity": "sha512-9pWYjivWC2lSvTCCUCgTc/62S8UKKkPFb0gYg0zYVcTsyRcIab1o0YoQYYLsI00QVES29x2VrBoid0ROPKk/RQ==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/axe-puppeteer/-/axe-puppeteer-1.0.0.tgz",
+ "integrity": "sha512-hTF3u4mtatgTN7fsLVyVgbRdNc15ngjDcTEuqhn9A7ugqLhLCryJWp9fzqZkNlrW8awPcxugyTwLPR7mRdPZmA==",
"dev": true,
"requires": {
"axe-core": "^3.1.2"
}
},
- "babel-code-frame": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
- "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
- "dev": true,
- "requires": {
- "chalk": "^1.1.3",
- "esutils": "^2.0.2",
- "js-tokens": "^3.0.2"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
- "dev": true
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "js-tokens": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
- "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
- "dev": true
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "^2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- }
- }
- },
- "babel-core": {
- "version": "7.0.0-bridge.0",
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
- "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
- "dev": true
- },
"babel-eslint": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.0.3.tgz",
@@ -3750,50 +3689,189 @@
}
}
},
- "babel-generator": {
- "version": "6.26.1",
- "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
- "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+ "babel-jest": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.1.0.tgz",
+ "integrity": "sha512-MLcagnVrO9ybQGLEfZUqnOzv36iQzU7Bj4elm39vCukumLVSfoX+tRy3/jW7lUKc7XdpRmB/jech6L/UCsSZjw==",
"dev": true,
"requires": {
- "babel-messages": "^6.23.0",
- "babel-runtime": "^6.26.0",
- "babel-types": "^6.26.0",
- "detect-indent": "^4.0.0",
- "jsesc": "^1.3.0",
- "lodash": "^4.17.4",
- "source-map": "^0.5.7",
- "trim-right": "^1.0.1"
+ "babel-plugin-istanbul": "^5.1.0",
+ "babel-preset-jest": "^24.1.0",
+ "chalk": "^2.4.2",
+ "slash": "^2.0.0"
},
"dependencies": {
- "jsesc": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
- "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+ "babel-plugin-istanbul": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz",
+ "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0",
+ "istanbul-lib-instrument": "^3.0.0",
+ "test-exclude": "^5.0.0"
+ }
+ },
+ "babel-plugin-jest-hoist": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.1.0.tgz",
+ "integrity": "sha512-gljYrZz8w1b6fJzKcsfKsipSru2DU2DmQ39aB6nV3xQ0DDv3zpIzKGortA5gknrhNnPN8DweaEgrnZdmbGmhnw==",
+ "dev": true
+ },
+ "babel-preset-jest": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.1.0.tgz",
+ "integrity": "sha512-FfNLDxFWsNX9lUmtwY7NheGlANnagvxq8LZdl5PKnVG3umP+S/g0XbVBfwtA4Ai3Ri/IMkWabBz3Tyk9wdspcw==",
+ "dev": true,
+ "requires": {
+ "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
+ "babel-plugin-jest-hoist": "^24.1.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "istanbul-lib-instrument": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz",
+ "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "istanbul-lib-coverage": "^2.0.3",
+ "semver": "^5.5.0"
+ }
+ },
+ "load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
+ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz",
+ "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0",
+ "read-pkg": "^3.0.0"
+ }
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "test-exclude": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz",
+ "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==",
+ "dev": true,
+ "requires": {
+ "arrify": "^1.0.1",
+ "minimatch": "^3.0.4",
+ "read-pkg-up": "^4.0.0",
+ "require-main-filename": "^1.0.1"
+ }
}
}
},
- "babel-helpers": {
- "version": "6.24.1",
- "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
- "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
- "dev": true,
- "requires": {
- "babel-runtime": "^6.22.0",
- "babel-template": "^6.24.1"
- }
- },
- "babel-jest": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-23.6.0.tgz",
- "integrity": "sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew==",
- "dev": true,
- "requires": {
- "babel-plugin-istanbul": "^4.1.6",
- "babel-preset-jest": "^23.2.0"
- }
- },
"babel-loader": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.5.tgz",
@@ -3871,227 +3949,40 @@
}
}
},
- "babel-messages": {
- "version": "6.23.0",
- "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
- "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
- "dev": true,
- "requires": {
- "babel-runtime": "^6.22.0"
- }
- },
- "babel-plugin-istanbul": {
- "version": "4.1.6",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
- "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==",
- "dev": true,
- "requires": {
- "babel-plugin-syntax-object-rest-spread": "^6.13.0",
- "find-up": "^2.1.0",
- "istanbul-lib-instrument": "^1.10.1",
- "test-exclude": "^4.2.1"
- }
- },
- "babel-plugin-jest-hoist": {
- "version": "23.2.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz",
- "integrity": "sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=",
+ "bail": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz",
+ "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==",
"dev": true
},
- "babel-plugin-syntax-object-rest-spread": {
- "version": "6.13.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
- "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
- "babel-preset-jest": {
- "version": "23.2.0",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz",
- "integrity": "sha1-jsegOhOPABoaj7HoETZSvxpV2kY=",
- "dev": true,
- "requires": {
- "babel-plugin-jest-hoist": "^23.2.0",
- "babel-plugin-syntax-object-rest-spread": "^6.13.0"
- }
- },
- "babel-register": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
- "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
"dev": true,
"requires": {
- "babel-core": "^6.26.0",
- "babel-runtime": "^6.26.0",
- "core-js": "^2.5.0",
- "home-or-tmp": "^2.0.0",
- "lodash": "^4.17.4",
- "mkdirp": "^0.5.1",
- "source-map-support": "^0.4.15"
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
},
"dependencies": {
- "babel-core": {
- "version": "6.26.3",
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
- "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
- "dev": true,
- "requires": {
- "babel-code-frame": "^6.26.0",
- "babel-generator": "^6.26.0",
- "babel-helpers": "^6.24.1",
- "babel-messages": "^6.23.0",
- "babel-register": "^6.26.0",
- "babel-runtime": "^6.26.0",
- "babel-template": "^6.26.0",
- "babel-traverse": "^6.26.0",
- "babel-types": "^6.26.0",
- "babylon": "^6.18.0",
- "convert-source-map": "^1.5.1",
- "debug": "^2.6.9",
- "json5": "^0.5.1",
- "lodash": "^4.17.4",
- "minimatch": "^3.0.4",
- "path-is-absolute": "^1.0.1",
- "private": "^0.1.8",
- "slash": "^1.0.0",
- "source-map": "^0.5.7"
- }
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
- },
- "babel-runtime": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
- "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
- "dev": true,
- "requires": {
- "core-js": "^2.4.0",
- "regenerator-runtime": "^0.11.0"
- },
- "dependencies": {
- "regenerator-runtime": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
- "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
- "dev": true
- }
- }
- },
- "babel-template": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
- "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
- "dev": true,
- "requires": {
- "babel-runtime": "^6.26.0",
- "babel-traverse": "^6.26.0",
- "babel-types": "^6.26.0",
- "babylon": "^6.18.0",
- "lodash": "^4.17.4"
- }
- },
- "babel-traverse": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
- "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
- "dev": true,
- "requires": {
- "babel-code-frame": "^6.26.0",
- "babel-messages": "^6.23.0",
- "babel-runtime": "^6.26.0",
- "babel-types": "^6.26.0",
- "babylon": "^6.18.0",
- "debug": "^2.6.8",
- "globals": "^9.18.0",
- "invariant": "^2.2.2",
- "lodash": "^4.17.4"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "globals": {
- "version": "9.18.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
- "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
- "dev": true
- }
- }
- },
- "babel-types": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
- "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
- "dev": true,
- "requires": {
- "babel-runtime": "^6.26.0",
- "esutils": "^2.0.2",
- "lodash": "^4.17.4",
- "to-fast-properties": "^1.0.3"
- },
- "dependencies": {
- "to-fast-properties": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
- "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
- "dev": true
- }
- }
- },
- "babylon": {
- "version": "6.18.0",
- "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
- "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
- "dev": true
- },
- "bail": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz",
- "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==",
- "dev": true
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true
- },
- "base": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
- "dev": true,
- "requires": {
- "cache-base": "^1.0.1",
- "class-utils": "^0.3.5",
- "component-emitter": "^1.2.1",
- "define-property": "^1.0.0",
- "isobject": "^3.0.1",
- "mixin-deep": "^1.2.0",
- "pascalcase": "^0.1.1"
- },
- "dependencies": {
- "define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
- "dev": true,
- "requires": {
- "is-descriptor": "^1.0.0"
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
}
},
"is-accessor-descriptor": {
@@ -4626,9 +4517,9 @@
"dev": true
},
"callsites": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
- "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
"dev": true
},
"camelcase": {
@@ -5435,12 +5326,6 @@
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
"dev": true
},
- "circular-json-es6": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/circular-json-es6/-/circular-json-es6-2.0.2.tgz",
- "integrity": "sha512-ODYONMMNb3p658Zv+Pp+/XPa5s6q7afhz3Tzyvo+VRh9WIrJ64J76ZC4GQxnlye/NesTn09jvOiuE8+xxfpwhQ==",
- "dev": true
- },
"clap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
@@ -5834,6 +5719,12 @@
}
}
},
+ "compare-versions": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz",
+ "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==",
+ "dev": true
+ },
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
@@ -7019,15 +6910,6 @@
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
"dev": true
},
- "deep-equal-ident": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz",
- "integrity": "sha1-BvS4nlNxDNbOpKd4HHqVZkLejck=",
- "dev": true,
- "requires": {
- "lodash.isequal": "^3.0"
- }
- },
"deep-freeze": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz",
@@ -7046,12 +6928,20 @@
"integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ=="
},
"default-require-extensions": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz",
- "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
+ "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
"dev": true,
"requires": {
- "strip-bom": "^2.0.0"
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ }
}
},
"defaults": {
@@ -7158,15 +7048,6 @@
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"dev": true
},
- "detect-indent": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
- "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
- "dev": true,
- "requires": {
- "repeating": "^2.0.0"
- }
- },
"detect-newline": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
@@ -7188,6 +7069,12 @@
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
},
+ "diff-sequences": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.0.0.tgz",
+ "integrity": "sha512-46OkIuVGBBnrC0soO/4LHu5LHGHx0uhP65OVz8XOrAJpqiCB2aVIuESvjI1F9oqebuvY8lekS1pt6TN7vt7qsw==",
+ "dev": true
+ },
"diffie-hellman": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@@ -7421,18 +7308,20 @@
"dev": true
},
"enzyme": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.7.0.tgz",
- "integrity": "sha512-QLWx+krGK6iDNyR1KlH5YPZqxZCQaVF6ike1eDJAOg0HvSkSCVImPsdWaNw6v+VrnK92Kg8jIOYhuOSS9sBpyg==",
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.9.0.tgz",
+ "integrity": "sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg==",
"dev": true,
"requires": {
"array.prototype.flat": "^1.2.1",
"cheerio": "^1.0.0-rc.2",
"function.prototype.name": "^1.1.0",
"has": "^1.0.3",
+ "html-element-map": "^1.0.0",
"is-boolean-object": "^1.0.0",
"is-callable": "^1.1.4",
"is-number-object": "^1.0.3",
+ "is-regex": "^1.0.4",
"is-string": "^1.0.4",
"is-subset": "^0.1.1",
"lodash.escape": "^4.0.1",
@@ -7445,76 +7334,105 @@
"raf": "^3.4.0",
"rst-selector-parser": "^2.2.3",
"string.prototype.trim": "^1.1.2"
- },
- "dependencies": {
- "lodash.isequal": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
- "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
- "dev": true
- }
}
},
"enzyme-adapter-react-16": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.6.0.tgz",
- "integrity": "sha512-ay9eGFpChyUDnjTFMMJHzrb681LF3hPWJLEA7RoLFG9jSWAdAm2V50pGmFV9dYGJgh5HfdiqM+MNvle41Yf/PA==",
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz",
+ "integrity": "sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg==",
"dev": true,
"requires": {
- "enzyme-adapter-utils": "^1.8.0",
+ "enzyme-adapter-utils": "^1.10.0",
"function.prototype.name": "^1.1.0",
"object.assign": "^4.1.0",
- "object.values": "^1.0.4",
+ "object.values": "^1.1.0",
"prop-types": "^15.6.2",
- "react-is": "^16.5.2",
+ "react-is": "^16.7.0",
"react-test-renderer": "^16.0.0-0"
},
"dependencies": {
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "object.values": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+ "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
"prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dev": true,
"requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
}
},
"react-is": {
- "version": "16.6.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.0.tgz",
- "integrity": "sha512-q8U7k0Fi7oxF1HvQgyBjPwDXeMplEsArnKt2iYhuIF86+GBbgLHdAmokL3XUFjTd7Q363OSNG55FOGUdONVn1g==",
+ "version": "16.8.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz",
+ "integrity": "sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q==",
"dev": true
}
}
},
"enzyme-adapter-utils": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.8.1.tgz",
- "integrity": "sha512-s3QB3xQAowaDS2sHhmEqrT13GJC4+n5bG015ZkLv60n9k5vhxxHTQRIneZmQ4hmdCZEBrvUJ89PG6fRI5OEeuQ==",
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz",
+ "integrity": "sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA==",
"dev": true,
"requires": {
"function.prototype.name": "^1.1.0",
"object.assign": "^4.1.0",
- "prop-types": "^15.6.2"
+ "object.fromentries": "^2.0.0",
+ "prop-types": "^15.6.2",
+ "semver": "^5.6.0"
},
"dependencies": {
"prop-types": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
- "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dev": true,
"requires": {
- "loose-envify": "^1.3.1",
- "object-assign": "^4.1.1"
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
}
+ },
+ "react-is": {
+ "version": "16.8.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz",
+ "integrity": "sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
}
}
},
"enzyme-to-json": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.4.tgz",
- "integrity": "sha1-Z8YEDpMRgvGDQYry659DIyWKp38=",
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.5.tgz",
+ "integrity": "sha512-DmH1wJ68HyPqKSYXdQqB33ZotwfUhwQZW3IGXaNXgR69Iodaoj8TF/D9RjLdz4pEhGq2Tx2zwNUIjBuqoZeTgA==",
"dev": true,
"requires": {
"lodash": "^4.17.4"
@@ -7920,32 +7838,6 @@
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"dev": true
},
- "event-stream": {
- "version": "3.3.4",
- "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
- "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
- "dev": true,
- "requires": {
- "duplexer": "~0.1.1",
- "from": "~0",
- "map-stream": "~0.1.0",
- "pause-stream": "0.0.11",
- "split": "0.3",
- "stream-combiner": "~0.0.4",
- "through": "~2.3.1"
- },
- "dependencies": {
- "split": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
- "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
- "dev": true,
- "requires": {
- "through": "2"
- }
- }
- }
- },
"events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
@@ -8044,232 +7936,91 @@
}
}
},
- "expand-range": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
- "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
+ "expect": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-24.1.0.tgz",
+ "integrity": "sha512-lVcAPhaYkQcIyMS+F8RVwzbm1jro20IG8OkvxQ6f1JfqhVZyyudCwYogQ7wnktlf14iF3ii7ArIUO/mqvrW9Gw==",
"dev": true,
"requires": {
- "fill-range": "^2.1.0"
+ "ansi-styles": "^3.2.0",
+ "jest-get-type": "^24.0.0",
+ "jest-matcher-utils": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-regex-util": "^24.0.0"
},
"dependencies": {
- "fill-range": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
- "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
- "dev": true,
- "requires": {
- "is-number": "^2.1.0",
- "isobject": "^2.0.0",
- "randomatic": "^3.0.0",
- "repeat-element": "^1.1.2",
- "repeat-string": "^1.5.2"
- }
- },
- "is-number": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
- "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- }
- },
- "isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "jest-message-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "isarray": "1.0.0"
+ "@babel/code-frame": "^7.0.0",
+ "chalk": "^2.0.1",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
+ "stack-utils": "^1.0.1"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
}
}
},
- "expect": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/expect/-/expect-23.6.0.tgz",
- "integrity": "sha512-dgSoOHgmtn/aDGRVFWclQyPDKl2CQRq0hmIEoUAuQs/2rn2NcvCWcSCovm6BLeuB/7EZuLGu2QfnR+qRt5OM4w==",
+ "expect-puppeteer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/expect-puppeteer/-/expect-puppeteer-4.0.0.tgz",
+ "integrity": "sha512-K7D6WRUWR0CtQLqf9ZMBQNkpbhQzxWkKEt9/PnVl+aWMF0xcdV7JW9AClxNprzXkrUq/ILws3H2u6dWcEEjRSQ==",
+ "dev": true
+ },
+ "express": {
+ "version": "4.16.3",
+ "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz",
+ "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
"dev": true,
"requires": {
- "ansi-styles": "^3.2.0",
- "jest-diff": "^23.6.0",
- "jest-get-type": "^22.1.0",
- "jest-matcher-utils": "^23.6.0",
- "jest-message-util": "^23.4.0",
- "jest-regex-util": "^23.3.0"
+ "accepts": "~1.3.5",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.18.2",
+ "content-disposition": "0.5.2",
+ "content-type": "~1.0.4",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.1.1",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.3",
+ "qs": "6.5.1",
+ "range-parser": "~1.2.0",
+ "safe-buffer": "5.1.1",
+ "send": "0.16.2",
+ "serve-static": "1.13.2",
+ "setprototypeof": "1.1.0",
+ "statuses": "~1.4.0",
+ "type-is": "~1.6.16",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
- "arr-flatten": "^1.0.1"
- }
- },
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
- "dev": true
- },
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
- }
- },
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "^0.1.0"
- }
- },
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
- "dev": true,
- "requires": {
- "is-extglob": "^1.0.0"
- }
- },
- "jest-matcher-utils": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz",
- "integrity": "sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.1",
- "jest-get-type": "^22.1.0",
- "pretty-format": "^23.6.0"
- }
- },
- "jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
- "chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
- "stack-utils": "^1.0.1"
- }
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- },
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
- "dev": true,
- "requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
- }
- },
- "pretty-format": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz",
- "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==",
- "dev": true,
- "requires": {
- "ansi-regex": "^3.0.0",
- "ansi-styles": "^3.2.0"
- }
- }
- }
- },
- "expect-puppeteer": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/expect-puppeteer/-/expect-puppeteer-3.5.1.tgz",
- "integrity": "sha512-SB5JeJCXWSRcUK39fBJlCA6qnVt3BG1/M9vYZ+XYq8gY9jab9Jm4BztsrAwDTWca1L+O/7dRYrG2BPziRtjh+Q==",
- "dev": true
- },
- "express": {
- "version": "4.16.3",
- "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz",
- "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
- "dev": true,
- "requires": {
- "accepts": "~1.3.5",
- "array-flatten": "1.1.1",
- "body-parser": "1.18.2",
- "content-disposition": "0.5.2",
- "content-type": "~1.0.4",
- "cookie": "0.3.1",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.1.1",
- "fresh": "0.5.2",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.2",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.3",
- "qs": "6.5.1",
- "range-parser": "~1.2.0",
- "safe-buffer": "5.1.1",
- "send": "0.16.2",
- "serve-static": "1.13.2",
- "setprototypeof": "1.1.0",
- "statuses": "~1.4.0",
- "type-is": "~1.6.16",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
+ "ms": "2.0.0"
}
},
"qs": {
@@ -8572,12 +8323,6 @@
"object-assign": "^4.0.1"
}
},
- "filename-regex": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
- "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
- "dev": true
- },
"fileset": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
@@ -8881,12 +8626,6 @@
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"dev": true
},
- "from": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
- "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
- "dev": true
- },
"from2": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
@@ -9972,25 +9711,6 @@
"path-is-absolute": "^1.0.0"
}
},
- "glob-base": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
- "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
- "dev": true,
- "requires": {
- "glob-parent": "^2.0.0",
- "is-glob": "^2.0.0"
- }
- },
- "glob-parent": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
- "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
- "dev": true,
- "requires": {
- "is-glob": "^2.0.0"
- }
- },
"glob-to-regexp": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
@@ -10260,16 +9980,6 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
- "home-or-tmp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
- "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
- "dev": true,
- "requires": {
- "os-homedir": "^1.0.0",
- "os-tmpdir": "^1.0.1"
- }
- },
"homedir-polyfill": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
@@ -10314,6 +10024,15 @@
"integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=",
"dev": true
},
+ "html-element-map": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.0.0.tgz",
+ "integrity": "sha512-/SP6aOiM5Ai9zALvCxDubIeez0LvG3qP7R9GcRDnJEP/HBmv0A8A9K0o8+HFudcFt46+i921ANjzKsjPjb7Enw==",
+ "dev": true,
+ "requires": {
+ "array-filter": "^1.0.0"
+ }
+ },
"html-encoding-sniffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
@@ -10812,12 +10531,6 @@
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
"dev": true
},
- "is-dotfile": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
- "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
- "dev": true
- },
"is-equal-shallow": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
@@ -10833,12 +10546,6 @@
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
"dev": true
},
- "is-extglob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
- "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
- "dev": true
- },
"is-finite": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
@@ -10854,20 +10561,11 @@
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-generator-fn": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz",
- "integrity": "sha1-lp1J4bszKfa7fwkIm+JleLLd1Go=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.0.0.tgz",
+ "integrity": "sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g==",
"dev": true
},
- "is-glob": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
- "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
- "dev": true,
- "requires": {
- "is-extglob": "^1.0.0"
- }
- },
"is-hexadecimal": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz",
@@ -10930,12 +10628,6 @@
"isobject": "^3.0.1"
}
},
- "is-posix-bracket": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
- "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
- "dev": true
- },
"is-primitive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
@@ -11090,630 +10782,649 @@
"dev": true
},
"istanbul-api": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz",
- "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==",
- "dev": true,
- "requires": {
- "async": "^2.1.4",
- "fileset": "^2.0.2",
- "istanbul-lib-coverage": "^1.2.1",
- "istanbul-lib-hook": "^1.2.2",
- "istanbul-lib-instrument": "^1.10.2",
- "istanbul-lib-report": "^1.1.5",
- "istanbul-lib-source-maps": "^1.2.6",
- "istanbul-reports": "^1.5.1",
- "js-yaml": "^3.7.0",
- "mkdirp": "^0.5.1",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz",
+ "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.1",
+ "compare-versions": "^3.2.1",
+ "fileset": "^2.0.3",
+ "istanbul-lib-coverage": "^2.0.3",
+ "istanbul-lib-hook": "^2.0.3",
+ "istanbul-lib-instrument": "^3.1.0",
+ "istanbul-lib-report": "^2.0.4",
+ "istanbul-lib-source-maps": "^3.0.2",
+ "istanbul-reports": "^2.1.1",
+ "js-yaml": "^3.12.0",
+ "make-dir": "^1.3.0",
+ "minimatch": "^3.0.4",
"once": "^1.4.0"
},
"dependencies": {
- "istanbul-lib-coverage": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz",
- "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==",
- "dev": true
- },
"istanbul-lib-instrument": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz",
- "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz",
+ "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==",
"dev": true,
"requires": {
- "babel-generator": "^6.18.0",
- "babel-template": "^6.16.0",
- "babel-traverse": "^6.18.0",
- "babel-types": "^6.18.0",
- "babylon": "^6.18.0",
- "istanbul-lib-coverage": "^1.2.1",
- "semver": "^5.3.0"
+ "@babel/generator": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "istanbul-lib-coverage": "^2.0.3",
+ "semver": "^5.5.0"
}
}
}
},
"istanbul-lib-coverage": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz",
- "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+ "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==",
"dev": true
},
"istanbul-lib-hook": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz",
- "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==",
- "dev": true,
- "requires": {
- "append-transform": "^0.4.0"
- }
- },
- "istanbul-lib-instrument": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz",
- "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz",
+ "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==",
"dev": true,
"requires": {
- "babel-generator": "^6.18.0",
- "babel-template": "^6.16.0",
- "babel-traverse": "^6.18.0",
- "babel-types": "^6.18.0",
- "babylon": "^6.18.0",
- "istanbul-lib-coverage": "^1.2.1",
- "semver": "^5.3.0"
- },
- "dependencies": {
- "istanbul-lib-coverage": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz",
- "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==",
- "dev": true
- }
+ "append-transform": "^1.0.0"
}
},
"istanbul-lib-report": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz",
- "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==",
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz",
+ "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==",
"dev": true,
"requires": {
- "istanbul-lib-coverage": "^1.2.1",
- "mkdirp": "^0.5.1",
- "path-parse": "^1.0.5",
- "supports-color": "^3.1.2"
+ "istanbul-lib-coverage": "^2.0.3",
+ "make-dir": "^1.3.0",
+ "supports-color": "^6.0.0"
},
"dependencies": {
- "has-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
- "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
- "dev": true
- },
- "istanbul-lib-coverage": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz",
- "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==",
- "dev": true
- },
"supports-color": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
- "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dev": true,
"requires": {
- "has-flag": "^1.0.0"
+ "has-flag": "^3.0.0"
}
}
}
},
"istanbul-lib-source-maps": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz",
- "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz",
+ "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==",
"dev": true,
"requires": {
- "debug": "^3.1.0",
- "istanbul-lib-coverage": "^1.2.1",
- "mkdirp": "^0.5.1",
- "rimraf": "^2.6.1",
- "source-map": "^0.5.3"
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^2.0.3",
+ "make-dir": "^1.3.0",
+ "rimraf": "^2.6.2",
+ "source-map": "^0.6.1"
},
"dependencies": {
- "istanbul-lib-coverage": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz",
- "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==",
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"istanbul-reports": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz",
- "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz",
+ "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==",
"dev": true,
"requires": {
- "handlebars": "^4.0.3"
+ "handlebars": "^4.1.0"
}
},
"jest": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest/-/jest-23.6.0.tgz",
- "integrity": "sha512-lWzcd+HSiqeuxyhG+EnZds6iO3Y3ZEnMrfZq/OTGvF/C+Z4fPMCdhWTGSAiO2Oym9rbEXfwddHhh6jqrTF3+Lw==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-24.1.0.tgz",
+ "integrity": "sha512-+q91L65kypqklvlRFfXfdzUKyngQLOcwGhXQaLmVHv+d09LkNXuBuGxlofTFW42XMzu3giIcChchTsCNUjQ78A==",
"dev": true,
"requires": {
- "import-local": "^1.0.0",
- "jest-cli": "^23.6.0"
+ "import-local": "^2.0.0",
+ "jest-cli": "^24.1.0"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.0.1"
- }
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
},
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
"dev": true
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
- }
+ "camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
- "is-posix-bracket": "^0.1.0"
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
}
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "import-local": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+ "dev": true,
+ "requires": {
+ "pkg-dir": "^3.0.0",
+ "resolve-cwd": "^2.0.0"
+ }
+ },
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "ci-info": "^2.0.0"
+ }
+ },
+ "istanbul-lib-instrument": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz",
+ "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "istanbul-lib-coverage": "^2.0.3",
+ "semver": "^5.5.0"
}
},
"jest-cli": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-23.6.0.tgz",
- "integrity": "sha512-hgeD1zRUp1E1zsiyOXjEn4LzRLWdJBV//ukAHGlx6s5mfCNJTbhbHjgxnDUXA8fsKWN/HqFFF6X5XcCwC/IvYQ==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.1.0.tgz",
+ "integrity": "sha512-U/iyWPwOI0T1CIxVLtk/2uviOTJ/OiSWJSe8qt6X1VkbbgP+nrtLJlmT9lPBe4lK78VNFJtrJ7pttcNv/s7yCw==",
"dev": true,
"requires": {
"ansi-escapes": "^3.0.0",
"chalk": "^2.0.1",
"exit": "^0.1.2",
"glob": "^7.1.2",
- "graceful-fs": "^4.1.11",
- "import-local": "^1.0.0",
- "is-ci": "^1.0.10",
- "istanbul-api": "^1.3.1",
- "istanbul-lib-coverage": "^1.2.0",
- "istanbul-lib-instrument": "^1.10.1",
- "istanbul-lib-source-maps": "^1.2.4",
- "jest-changed-files": "^23.4.2",
- "jest-config": "^23.6.0",
- "jest-environment-jsdom": "^23.4.0",
- "jest-get-type": "^22.1.0",
- "jest-haste-map": "^23.6.0",
- "jest-message-util": "^23.4.0",
- "jest-regex-util": "^23.3.0",
- "jest-resolve-dependencies": "^23.6.0",
- "jest-runner": "^23.6.0",
- "jest-runtime": "^23.6.0",
- "jest-snapshot": "^23.6.0",
- "jest-util": "^23.4.0",
- "jest-validate": "^23.6.0",
- "jest-watcher": "^23.4.0",
- "jest-worker": "^23.2.0",
- "micromatch": "^2.3.11",
+ "graceful-fs": "^4.1.15",
+ "import-local": "^2.0.0",
+ "is-ci": "^2.0.0",
+ "istanbul-api": "^2.0.8",
+ "istanbul-lib-coverage": "^2.0.2",
+ "istanbul-lib-instrument": "^3.0.1",
+ "istanbul-lib-source-maps": "^3.0.1",
+ "jest-changed-files": "^24.0.0",
+ "jest-config": "^24.1.0",
+ "jest-environment-jsdom": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "jest-haste-map": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-regex-util": "^24.0.0",
+ "jest-resolve-dependencies": "^24.1.0",
+ "jest-runner": "^24.1.0",
+ "jest-runtime": "^24.1.0",
+ "jest-snapshot": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-validate": "^24.0.0",
+ "jest-watcher": "^24.0.0",
+ "jest-worker": "^24.0.0",
+ "micromatch": "^3.1.10",
"node-notifier": "^5.2.1",
- "prompts": "^0.1.9",
+ "p-each-series": "^1.0.0",
+ "pirates": "^4.0.0",
+ "prompts": "^2.0.1",
"realpath-native": "^1.0.0",
"rimraf": "^2.5.4",
- "slash": "^1.0.0",
+ "slash": "^2.0.0",
"string-length": "^2.0.0",
- "strip-ansi": "^4.0.0",
+ "strip-ansi": "^5.0.0",
"which": "^1.2.12",
- "yargs": "^11.0.0"
+ "yargs": "^12.0.2"
}
},
"jest-environment-jsdom": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz",
- "integrity": "sha1-BWp5UrP+pROsYqFAosNox52eYCM=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.0.0.tgz",
+ "integrity": "sha512-1YNp7xtxajTRaxbylDc2pWvFnfDTH5BJJGyVzyGAKNt/lEULohwEV9zFqTgG4bXRcq7xzdd+sGFws+LxThXXOw==",
"dev": true,
"requires": {
- "jest-mock": "^23.2.0",
- "jest-util": "^23.4.0",
+ "jest-mock": "^24.0.0",
+ "jest-util": "^24.0.0",
"jsdom": "^11.5.1"
}
},
"jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
+ "@babel/code-frame": "^7.0.0",
"chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
"stack-utils": "^1.0.1"
}
},
"jest-mock": {
- "version": "23.2.0",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-23.2.0.tgz",
- "integrity": "sha1-rRxg8p6HGdR8JuETgJi20YsmETQ=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.0.0.tgz",
+ "integrity": "sha512-sQp0Hu5fcf5NZEh1U9eIW2qD0BwJZjb63Yqd98PQJFvf/zzUTBoUAwv/Dc/HFeNHIw1f3hl/48vNn+j3STaI7A==",
"dev": true
},
"jest-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.4.0.tgz",
- "integrity": "sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
"dev": true,
"requires": {
- "callsites": "^2.0.0",
+ "callsites": "^3.0.0",
"chalk": "^2.0.1",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "jest-message-util": "^23.4.0",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
"mkdirp": "^0.5.1",
- "slash": "^1.0.0",
+ "slash": "^2.0.0",
"source-map": "^0.6.0"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "invert-kv": "^2.0.0"
}
},
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
}
},
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- },
- "yargs": {
- "version": "11.1.0",
- "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
- "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
+ "mem": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz",
+ "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
"dev": true,
"requires": {
- "cliui": "^4.0.0",
- "decamelize": "^1.1.1",
- "find-up": "^2.1.0",
- "get-caller-file": "^1.0.1",
- "os-locale": "^2.0.0",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^2.0.0",
- "which-module": "^2.0.0",
- "y18n": "^3.2.1",
- "yargs-parser": "^9.0.2"
+ "map-age-cleaner": "^0.1.1",
+ "mimic-fn": "^1.0.0",
+ "p-is-promise": "^2.0.0"
}
},
- "yargs-parser": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz",
- "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=",
- "dev": true,
- "requires": {
- "camelcase": "^4.1.0"
- }
- }
- }
- },
- "jest-changed-files": {
- "version": "23.4.2",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-23.4.2.tgz",
- "integrity": "sha512-EyNhTAUWEfwnK0Is/09LxoqNDOn7mU7S3EHskG52djOFS/z+IT0jT3h3Ql61+dklcG7bJJitIWEMB4Sp1piHmA==",
- "dev": true,
- "requires": {
- "throat": "^4.0.0"
- }
- },
- "jest-config": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-23.6.0.tgz",
- "integrity": "sha512-i8V7z9BeDXab1+VNo78WM0AtWpBRXJLnkT+lyT+Slx/cbP5sZJ0+NDuLcmBE5hXAoK0aUp7vI+MOxR+R4d8SRQ==",
- "dev": true,
- "requires": {
- "babel-core": "^6.0.0",
- "babel-jest": "^23.6.0",
- "chalk": "^2.0.1",
- "glob": "^7.1.1",
- "jest-environment-jsdom": "^23.4.0",
- "jest-environment-node": "^23.4.0",
- "jest-get-type": "^22.1.0",
- "jest-jasmine2": "^23.6.0",
- "jest-regex-util": "^23.3.0",
- "jest-resolve": "^23.6.0",
- "jest-util": "^23.4.0",
- "jest-validate": "^23.6.0",
- "micromatch": "^2.3.11",
- "pretty-format": "^23.6.0"
- },
- "dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "os-locale": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
- "arr-flatten": "^1.0.1"
+ "execa": "^1.0.0",
+ "lcid": "^2.0.0",
+ "mem": "^4.0.0"
}
},
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
- "dev": true
- },
- "babel-core": {
- "version": "6.26.3",
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
- "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
- "dev": true,
- "requires": {
- "babel-code-frame": "^6.26.0",
- "babel-generator": "^6.26.0",
- "babel-helpers": "^6.24.1",
- "babel-messages": "^6.23.0",
- "babel-register": "^6.26.0",
- "babel-runtime": "^6.26.0",
- "babel-template": "^6.26.0",
- "babel-traverse": "^6.26.0",
- "babel-types": "^6.26.0",
- "babylon": "^6.18.0",
- "convert-source-map": "^1.5.1",
- "debug": "^2.6.9",
- "json5": "^0.5.1",
- "lodash": "^4.17.4",
- "minimatch": "^3.0.4",
- "path-is-absolute": "^1.0.1",
- "private": "^0.1.8",
- "slash": "^1.0.0",
- "source-map": "^0.5.7"
- }
+ "p-is-promise": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz",
+ "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==",
+ "dev": true
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+ "p-limit": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
+ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
"dev": true,
"requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
+ "p-try": "^2.0.0"
}
},
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
- "ms": "2.0.0"
+ "p-limit": "^2.0.0"
}
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "^0.1.0"
- }
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "find-up": "^3.0.0"
}
},
- "jest-environment-jsdom": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz",
- "integrity": "sha1-BWp5UrP+pROsYqFAosNox52eYCM=",
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
- "jest-mock": "^23.2.0",
- "jest-util": "^23.4.0",
- "jsdom": "^11.5.1"
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
}
},
- "jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
- "chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
- "stack-utils": "^1.0.1"
- }
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
},
- "jest-mock": {
- "version": "23.2.0",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-23.2.0.tgz",
- "integrity": "sha1-rRxg8p6HGdR8JuETgJi20YsmETQ=",
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
- "jest-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.4.0.tgz",
- "integrity": "sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=",
+ "strip-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+ "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
"dev": true,
"requires": {
- "callsites": "^2.0.0",
- "chalk": "^2.0.1",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "jest-message-util": "^23.4.0",
- "mkdirp": "^0.5.1",
- "slash": "^1.0.0",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
+ "ansi-regex": "^4.0.0"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "yargs": {
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
"dev": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "cliui": "^4.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^11.1.1"
}
},
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+ "yargs-parser": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
+ "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
"dev": true,
"requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
}
}
}
},
- "jest-dev-server": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-3.6.0.tgz",
- "integrity": "sha512-UbDPDBjpD3t9hjZ6z4j1NW8+jYE1rP5jJ6qVLbWsnpPgfJDBziOhhUSspSvyCG3DW+txK8/Xtw1lwwiEponWpg==",
+ "jest-changed-files": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.0.0.tgz",
+ "integrity": "sha512-nnuU510R9U+UX0WNb5XFEcsrMqriSiRLeO9KWDFgPrpToaQm60prfQYpxsXigdClpvNot5bekDY440x9dNGnsQ==",
"dev": true,
"requires": {
- "chalk": "^2.4.1",
- "cwd": "^0.10.0",
- "find-process": "^1.2.1",
- "inquirer": "^6.2.0",
- "spawnd": "^3.5.2",
- "terminate": "^2.1.2",
- "wait-port": "^0.2.2"
+ "execa": "^1.0.0",
+ "throat": "^4.0.0"
},
"dependencies": {
- "ansi-regex": {
- "version": "4.0.0",
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ }
+ }
+ },
+ "jest-config": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.1.0.tgz",
+ "integrity": "sha512-FbbRzRqtFC6eGjG5VwsbW4E5dW3zqJKLWYiZWhB0/4E5fgsMw8GODLbGSrY5t17kKOtCWb/Z7nsIThRoDpuVyg==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.1.0",
+ "babel-jest": "^24.1.0",
+ "chalk": "^2.0.1",
+ "glob": "^7.1.1",
+ "jest-environment-jsdom": "^24.0.0",
+ "jest-environment-node": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "jest-jasmine2": "^24.1.0",
+ "jest-regex-util": "^24.0.0",
+ "jest-resolve": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-validate": "^24.0.0",
+ "micromatch": "^3.1.10",
+ "pretty-format": "^24.0.0",
+ "realpath-native": "^1.0.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
"integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
"dev": true
},
- "chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "dev": true
- },
- "external-editor": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
- "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
"dev": true,
"requires": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
}
+ }
+ }
+ },
+ "jest-dev-server": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-4.0.0.tgz",
+ "integrity": "sha512-tq3fHPM8BDbu/71yIxgGgZW62s1Em6rLNDce0/ff/4No093OyjUEPM8yIUaoBt4pxwwRGkaS1EZB5PzCmRLGkg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "cwd": "^0.10.0",
+ "find-process": "^1.2.1",
+ "inquirer": "^6.2.2",
+ "spawnd": "^4.0.0",
+ "tree-kill": "^1.2.1",
+ "wait-port": "^0.2.2"
+ },
+ "dependencies": {
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+ "dev": true
},
- "iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
- "safer-buffer": ">= 2.1.2 < 3"
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
}
},
"inquirer": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz",
- "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz",
+ "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==",
"dev": true,
"requires": {
- "ansi-escapes": "^3.0.0",
- "chalk": "^2.0.0",
+ "ansi-escapes": "^3.2.0",
+ "chalk": "^2.4.2",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
- "external-editor": "^3.0.0",
+ "external-editor": "^3.0.3",
"figures": "^2.0.0",
- "lodash": "^4.17.10",
+ "lodash": "^4.17.11",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
- "rxjs": "^6.1.0",
+ "rxjs": "^6.4.0",
"string-width": "^2.1.0",
"strip-ansi": "^5.0.0",
"through": "^2.3.6"
}
},
"rxjs": {
- "version": "6.3.3",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
- "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+ "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
@@ -11727,178 +11438,240 @@
"requires": {
"ansi-regex": "^4.0.0"
}
+ },
+ "tree-kill": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz",
+ "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==",
+ "dev": true
}
}
},
"jest-diff": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz",
- "integrity": "sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.0.0.tgz",
+ "integrity": "sha512-XY5wMpRaTsuMoU+1/B2zQSKQ9RdE9gsLkGydx3nvApeyPijLA8GtEvIcPwISRCer+VDf9W1mStTYYq6fPt8ryA==",
"dev": true,
"requires": {
"chalk": "^2.0.1",
- "diff": "^3.2.0",
- "jest-get-type": "^22.1.0",
- "pretty-format": "^23.6.0"
+ "diff-sequences": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "pretty-format": "^24.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
+ }
+ }
}
},
"jest-docblock": {
- "version": "23.2.0",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-23.2.0.tgz",
- "integrity": "sha1-8IXh8YVI2Z/dabICB+b9VdkTg6c=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.0.0.tgz",
+ "integrity": "sha512-KfAKZ4SN7CFOZpWg4i7g7MSlY0M+mq7K0aMqENaG2vHuhC9fc3vkpU/iNN9sOus7v3h3Y48uEjqz3+Gdn2iptA==",
"dev": true,
"requires": {
"detect-newline": "^2.1.0"
}
},
"jest-each": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-23.6.0.tgz",
- "integrity": "sha512-x7V6M/WGJo6/kLoissORuvLIeAoyo2YqLOoCDkohgJ4XOXSqOtyvr8FbInlAWS77ojBsZrafbozWoKVRdtxFCg==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.0.0.tgz",
+ "integrity": "sha512-gFcbY4Cu55yxExXMkjrnLXov3bWO3dbPAW7HXb31h/DNWdNc/6X8MtxGff8nh3/MjkF9DpVqnj0KsPKuPK0cpA==",
"dev": true,
"requires": {
"chalk": "^2.0.1",
- "pretty-format": "^23.6.0"
- }
- },
- "jest-environment-enzyme": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/jest-environment-enzyme/-/jest-environment-enzyme-6.1.2.tgz",
- "integrity": "sha512-WHeBKgBYOdryuOTEoK55lJwjg7Raery1OgXHLwukI3mSYgOkm2UrCDDT+vneqVgy7F8KuRHyStfD+TC/m2b7Kg==",
- "dev": true,
- "requires": {
- "jest-environment-jsdom": "^22.4.1"
+ "jest-get-type": "^24.0.0",
+ "jest-util": "^24.0.0",
+ "pretty-format": "^24.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+ "dev": true
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^2.0.0"
+ }
+ },
+ "jest-message-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "chalk": "^2.0.1",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
+ "stack-utils": "^1.0.1"
+ }
+ },
+ "jest-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0",
+ "chalk": "^2.0.1",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
+ "mkdirp": "^0.5.1",
+ "slash": "^2.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
+ }
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
}
},
"jest-environment-jsdom": {
- "version": "22.4.3",
- "resolved": "http://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz",
- "integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.0.0.tgz",
+ "integrity": "sha512-1YNp7xtxajTRaxbylDc2pWvFnfDTH5BJJGyVzyGAKNt/lEULohwEV9zFqTgG4bXRcq7xzdd+sGFws+LxThXXOw==",
"dev": true,
"requires": {
- "jest-mock": "^22.4.3",
- "jest-util": "^22.4.3",
+ "jest-mock": "^24.0.0",
+ "jest-util": "^24.0.0",
"jsdom": "^11.5.1"
}
},
"jest-environment-node": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-23.4.0.tgz",
- "integrity": "sha1-V+gO0IQd6jAxZ8zozXlSHeuv3hA=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.0.0.tgz",
+ "integrity": "sha512-62fOFcaEdU0VLaq8JL90TqwI7hLn0cOKOl8vY2n477vRkCJRojiRRtJVRzzCcgFvs6gqU97DNqX5R0BrBP6Rxg==",
"dev": true,
"requires": {
- "jest-mock": "^23.2.0",
- "jest-util": "^23.4.0"
+ "jest-mock": "^24.0.0",
+ "jest-util": "^24.0.0"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.0.1"
- }
- },
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
"dev": true
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
- }
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "^0.1.0"
- }
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "ci-info": "^2.0.0"
}
},
"jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
+ "@babel/code-frame": "^7.0.0",
"chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
"stack-utils": "^1.0.1"
}
},
"jest-mock": {
- "version": "23.2.0",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-23.2.0.tgz",
- "integrity": "sha1-rRxg8p6HGdR8JuETgJi20YsmETQ=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.0.0.tgz",
+ "integrity": "sha512-sQp0Hu5fcf5NZEh1U9eIW2qD0BwJZjb63Yqd98PQJFvf/zzUTBoUAwv/Dc/HFeNHIw1f3hl/48vNn+j3STaI7A==",
"dev": true
},
"jest-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.4.0.tgz",
- "integrity": "sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
"dev": true,
"requires": {
- "callsites": "^2.0.0",
+ "callsites": "^3.0.0",
"chalk": "^2.0.1",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "jest-message-util": "^23.4.0",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
"mkdirp": "^0.5.1",
- "slash": "^1.0.0",
+ "slash": "^2.0.0",
"source-map": "^0.6.0"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- },
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
- "dev": true,
- "requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
- }
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
},
"source-map": {
"version": "0.6.1",
@@ -11909,260 +11682,219 @@
}
},
"jest-environment-puppeteer": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/jest-environment-puppeteer/-/jest-environment-puppeteer-3.6.0.tgz",
- "integrity": "sha512-3ULqgH6f+HRu53wxP0NDFb8uZFxn2+97tK4eZicktgst/zWpSJucEpbsVVNWk4cIHrPo79rYoUfomxnui/ndAg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-puppeteer/-/jest-environment-puppeteer-4.0.0.tgz",
+ "integrity": "sha512-IdbfZW1TjT1lmdPlvlHi4S+CAHuGk63fzGUpQAUeadm77saSJISDMuYS5b7kZ6lXZtc4myu53TkMWDYhh60XOA==",
"dev": true,
"requires": {
- "chalk": "^2.4.1",
+ "chalk": "^2.4.2",
"cwd": "^0.10.0",
- "jest-dev-server": "^3.6.0",
+ "jest-dev-server": "^4.0.0",
"merge-deep": "^3.0.2"
- }
- },
- "jest-enzyme": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/jest-enzyme/-/jest-enzyme-6.1.2.tgz",
- "integrity": "sha512-+ds7r2ru3QkNJxelQ2tnC6d33pjUSsZHPD3v4TlnHlNMuGX3UKdxm5C46yZBvJICYBvIF+RFKBhLMM4evNM95Q==",
- "dev": true,
- "requires": {
- "enzyme-matchers": "^6.1.2",
- "enzyme-to-json": "^3.3.0",
- "jest-environment-enzyme": "^6.1.2"
},
"dependencies": {
- "enzyme-matchers": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/enzyme-matchers/-/enzyme-matchers-6.1.2.tgz",
- "integrity": "sha512-cP9p+HMOZ1ZXQ+k2H4dCkxmTZzIvpEy5zv0ZjgoBl6D0U43v+bJGH5IeWHdIovCzgJ0dVcMCKJ6lNu83lYUCAA==",
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
- "circular-json-es6": "^2.0.1",
- "deep-equal-ident": "^1.1.1"
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
}
}
}
},
"jest-get-type": {
- "version": "22.4.3",
- "resolved": "http://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
- "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.0.0.tgz",
+ "integrity": "sha512-z6/Eyf6s9ZDGz7eOvl+fzpuJmN9i0KyTt1no37/dHu8galssxz5ZEgnc1KaV8R31q1khxyhB4ui/X5ZjjPk77w==",
"dev": true
},
"jest-haste-map": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-23.6.0.tgz",
- "integrity": "sha512-uyNhMyl6dr6HaXGHp8VF7cK6KpC6G9z9LiMNsst+rJIZ8l7wY0tk8qwjPmEghczojZ2/ZhtEdIabZ0OQRJSGGg==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.0.0.tgz",
+ "integrity": "sha512-CcViJyUo41IQqttLxXVdI41YErkzBKbE6cS6dRAploCeutePYfUimWd3C9rQEWhX0YBOQzvNsC0O9nYxK2nnxQ==",
"dev": true,
"requires": {
"fb-watchman": "^2.0.0",
- "graceful-fs": "^4.1.11",
+ "graceful-fs": "^4.1.15",
"invariant": "^2.2.4",
- "jest-docblock": "^23.2.0",
- "jest-serializer": "^23.0.1",
- "jest-worker": "^23.2.0",
- "micromatch": "^2.3.11",
- "sane": "^2.0.0"
+ "jest-serializer": "^24.0.0",
+ "jest-util": "^24.0.0",
+ "jest-worker": "^24.0.0",
+ "micromatch": "^3.1.10",
+ "sane": "^3.0.0"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.0.1"
- }
- },
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
"dev": true
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
- }
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "^0.1.0"
- }
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "ci-info": "^2.0.0"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "jest-message-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "@babel/code-frame": "^7.0.0",
+ "chalk": "^2.0.1",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
+ "stack-utils": "^1.0.1"
}
},
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+ "jest-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
"dev": true,
"requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
+ "callsites": "^3.0.0",
+ "chalk": "^2.0.1",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
+ "mkdirp": "^0.5.1",
+ "slash": "^2.0.0",
+ "source-map": "^0.6.0"
}
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
}
}
},
"jest-jasmine2": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-23.6.0.tgz",
- "integrity": "sha512-pe2Ytgs1nyCs8IvsEJRiRTPC0eVYd8L/dXJGU08GFuBwZ4sYH/lmFDdOL3ZmvJR8QKqV9MFuwlsAi/EWkFUbsQ==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.1.0.tgz",
+ "integrity": "sha512-H+o76SdSNyCh9fM5K8upK45YTo/DiFx5w2YAzblQebSQmukDcoVBVeXynyr7DDnxh+0NTHYRCLwJVf3tC518wg==",
"dev": true,
"requires": {
- "babel-traverse": "^6.0.0",
+ "@babel/traverse": "^7.1.0",
"chalk": "^2.0.1",
"co": "^4.6.0",
- "expect": "^23.6.0",
- "is-generator-fn": "^1.0.0",
- "jest-diff": "^23.6.0",
- "jest-each": "^23.6.0",
- "jest-matcher-utils": "^23.6.0",
- "jest-message-util": "^23.4.0",
- "jest-snapshot": "^23.6.0",
- "jest-util": "^23.4.0",
- "pretty-format": "^23.6.0"
+ "expect": "^24.1.0",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^24.0.0",
+ "jest-matcher-utils": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-snapshot": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "pretty-format": "^24.0.0",
+ "throat": "^4.0.0"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.0.1"
- }
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
},
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
"dev": true
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
- }
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "^0.1.0"
- }
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "ci-info": "^2.0.0"
}
},
"jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
+ "@babel/code-frame": "^7.0.0",
"chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
"stack-utils": "^1.0.1"
}
},
"jest-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.4.0.tgz",
- "integrity": "sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
"dev": true,
"requires": {
- "callsites": "^2.0.0",
+ "callsites": "^3.0.0",
"chalk": "^2.0.1",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "jest-message-util": "^23.4.0",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
"mkdirp": "^0.5.1",
- "slash": "^1.0.0",
+ "slash": "^2.0.0",
"source-map": "^0.6.0"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
"dev": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
}
},
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
- "dev": true,
- "requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
- }
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
},
"source-map": {
"version": "0.6.1",
@@ -12173,140 +11905,109 @@
}
},
"jest-leak-detector": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-23.6.0.tgz",
- "integrity": "sha512-f/8zA04rsl1Nzj10HIyEsXvYlMpMPcy0QkQilVZDFOaPbv2ur71X5u2+C4ZQJGyV/xvVXtCCZ3wQ99IgQxftCg==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.0.0.tgz",
+ "integrity": "sha512-ZYHJYFeibxfsDSKowjDP332pStuiFT2xfc5R67Rjm/l+HFJWJgNIOCOlQGeXLCtyUn3A23+VVDdiCcnB6dTTrg==",
"dev": true,
"requires": {
- "pretty-format": "^23.6.0"
+ "pretty-format": "^24.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
+ }
+ }
}
},
"jest-matcher-utils": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz",
- "integrity": "sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.0.0.tgz",
+ "integrity": "sha512-LQTDmO+aWRz1Tf9HJg+HlPHhDh1E1c65kVwRFo5mwCVp5aQDzlkz4+vCvXhOKFjitV2f0kMdHxnODrXVoi+rlA==",
"dev": true,
"requires": {
"chalk": "^2.0.1",
- "jest-get-type": "^22.1.0",
- "pretty-format": "^23.6.0"
+ "jest-diff": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "pretty-format": "^24.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
+ }
+ }
}
},
"jest-message-util": {
- "version": "22.4.3",
- "resolved": "http://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz",
- "integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
+ "@babel/code-frame": "^7.0.0",
"chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
"stack-utils": "^1.0.1"
},
"dependencies": {
- "arr-diff": {
+ "slash": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.0.1"
- }
- },
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
- },
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
- }
- },
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "^0.1.0"
- }
- },
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
- "dev": true,
- "requires": {
- "is-extglob": "^1.0.0"
- }
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- },
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
- "dev": true,
- "requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
- }
}
}
},
"jest-mock": {
- "version": "22.4.3",
- "resolved": "http://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz",
- "integrity": "sha512-+4R6mH5M1G4NK16CKg9N1DtCaFmuxhcIqF4lQK/Q1CIotqMs/XBemfpDPeVZBFow6iyUNu6EBT9ugdNOTT5o5Q==",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.0.0.tgz",
+ "integrity": "sha512-sQp0Hu5fcf5NZEh1U9eIW2qD0BwJZjb63Yqd98PQJFvf/zzUTBoUAwv/Dc/HFeNHIw1f3hl/48vNn+j3STaI7A==",
"dev": true
},
"jest-puppeteer": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/jest-puppeteer/-/jest-puppeteer-3.2.1.tgz",
- "integrity": "sha1-cvasvoLy/eFnjwaTEGWD7ec0560=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jest-puppeteer/-/jest-puppeteer-4.0.0.tgz",
+ "integrity": "sha512-B/5BlePsYmxTVmWD0E3zEuYfrTPk66EZskRlZhdDuXLoUAWPRu2+J/egnXh5GO+pUqkELffn5gfPulpn21rY7A==",
"dev": true,
"requires": {
- "expect-puppeteer": "^3.2.0",
- "jest-environment-puppeteer": "^3.2.1"
+ "expect-puppeteer": "^4.0.0",
+ "jest-environment-puppeteer": "^4.0.0"
}
},
"jest-regex-util": {
- "version": "23.3.0",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-23.3.0.tgz",
- "integrity": "sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.0.0.tgz",
+ "integrity": "sha512-Jv/uOTCuC+PY7WpJl2mpoI+WbY2ut73qwwO9ByJJNwOCwr1qWhEW2Lyi2S9ZewUdJqeVpEBisdEVZSI+Zxo58Q==",
"dev": true
},
"jest-resolve": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.6.0.tgz",
- "integrity": "sha512-XyoRxNtO7YGpQDmtQCmZjum1MljDqUCob7XlZ6jy9gsMugHdN2hY4+Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.1.0.tgz",
+ "integrity": "sha512-TPiAIVp3TG6zAxH28u/6eogbwrvZjBMWroSLBDkwkHKrqxB/RIdwkWDye4uqPlZIXWIaHtifY3L0/eO5Z0f2wg==",
"dev": true,
"requires": {
"browser-resolve": "^1.11.3",
@@ -12315,498 +12016,728 @@
}
},
"jest-resolve-dependencies": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-23.6.0.tgz",
- "integrity": "sha512-EkQWkFWjGKwRtRyIwRwI6rtPAEyPWlUC2MpzHissYnzJeHcyCn1Hc8j7Nn1xUVrS5C6W5+ZL37XTem4D4pLZdA==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.1.0.tgz",
+ "integrity": "sha512-2VwPsjd3kRPu7qe2cpytAgowCObk5AKeizfXuuiwgm1a9sijJDZe8Kh1sFj6FKvSaNEfCPlBVkZEJa2482m/Uw==",
"dev": true,
"requires": {
- "jest-regex-util": "^23.3.0",
- "jest-snapshot": "^23.6.0"
+ "jest-regex-util": "^24.0.0",
+ "jest-snapshot": "^24.1.0"
}
},
"jest-runner": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-23.6.0.tgz",
- "integrity": "sha512-kw0+uj710dzSJKU6ygri851CObtCD9cN8aNkg8jWJf4ewFyEa6kwmiH/r/M1Ec5IL/6VFa0wnAk6w+gzUtjJzA==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.1.0.tgz",
+ "integrity": "sha512-CDGOkT3AIFl16BLL/OdbtYgYvbAprwJ+ExKuLZmGSCSldwsuU2dEGauqkpvd9nphVdAnJUcP12e/EIlnTX0QXg==",
"dev": true,
"requires": {
+ "chalk": "^2.4.2",
"exit": "^0.1.2",
- "graceful-fs": "^4.1.11",
- "jest-config": "^23.6.0",
- "jest-docblock": "^23.2.0",
- "jest-haste-map": "^23.6.0",
- "jest-jasmine2": "^23.6.0",
- "jest-leak-detector": "^23.6.0",
- "jest-message-util": "^23.4.0",
- "jest-runtime": "^23.6.0",
- "jest-util": "^23.4.0",
- "jest-worker": "^23.2.0",
+ "graceful-fs": "^4.1.15",
+ "jest-config": "^24.1.0",
+ "jest-docblock": "^24.0.0",
+ "jest-haste-map": "^24.0.0",
+ "jest-jasmine2": "^24.1.0",
+ "jest-leak-detector": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-runtime": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-worker": "^24.0.0",
"source-map-support": "^0.5.6",
"throat": "^4.0.0"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.0.1"
- }
- },
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
"dev": true
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
}
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "^0.1.0"
- }
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "ci-info": "^2.0.0"
}
},
"jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
+ "@babel/code-frame": "^7.0.0",
"chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
"stack-utils": "^1.0.1"
}
},
"jest-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.4.0.tgz",
- "integrity": "sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
"dev": true,
"requires": {
- "callsites": "^2.0.0",
+ "callsites": "^3.0.0",
"chalk": "^2.0.1",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "jest-message-util": "^23.4.0",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
"mkdirp": "^0.5.1",
- "slash": "^1.0.0",
+ "slash": "^2.0.0",
"source-map": "^0.6.0"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- },
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
- "dev": true,
- "requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
- }
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
- },
- "source-map-support": {
- "version": "0.5.9",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
- "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
}
}
},
"jest-runtime": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-23.6.0.tgz",
- "integrity": "sha512-ycnLTNPT2Gv+TRhnAYAQ0B3SryEXhhRj1kA6hBPSeZaNQkJ7GbZsxOLUkwg6YmvWGdX3BB3PYKFLDQCAE1zNOw==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.1.0.tgz",
+ "integrity": "sha512-59/BY6OCuTXxGeDhEMU7+N33dpMQyXq7MLK07cNSIY/QYt2QZgJ7Tjx+rykBI0skAoigFl0A5tmT8UdwX92YuQ==",
"dev": true,
"requires": {
- "babel-core": "^6.0.0",
- "babel-plugin-istanbul": "^4.1.6",
+ "@babel/core": "^7.1.0",
+ "babel-plugin-istanbul": "^5.1.0",
"chalk": "^2.0.1",
"convert-source-map": "^1.4.0",
"exit": "^0.1.2",
"fast-json-stable-stringify": "^2.0.0",
- "graceful-fs": "^4.1.11",
- "jest-config": "^23.6.0",
- "jest-haste-map": "^23.6.0",
- "jest-message-util": "^23.4.0",
- "jest-regex-util": "^23.3.0",
- "jest-resolve": "^23.6.0",
- "jest-snapshot": "^23.6.0",
- "jest-util": "^23.4.0",
- "jest-validate": "^23.6.0",
- "micromatch": "^2.3.11",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.1.15",
+ "jest-config": "^24.1.0",
+ "jest-haste-map": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-regex-util": "^24.0.0",
+ "jest-resolve": "^24.1.0",
+ "jest-snapshot": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-validate": "^24.0.0",
+ "micromatch": "^3.1.10",
"realpath-native": "^1.0.0",
- "slash": "^1.0.0",
- "strip-bom": "3.0.0",
- "write-file-atomic": "^2.1.0",
- "yargs": "^11.0.0"
+ "slash": "^2.0.0",
+ "strip-bom": "^3.0.0",
+ "write-file-atomic": "2.4.1",
+ "yargs": "^12.0.2"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "babel-plugin-istanbul": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz",
+ "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==",
"dev": true,
"requires": {
- "arr-flatten": "^1.0.1"
- }
- },
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
- "dev": true
- },
- "babel-core": {
- "version": "6.26.3",
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
- "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
- "dev": true,
- "requires": {
- "babel-code-frame": "^6.26.0",
- "babel-generator": "^6.26.0",
- "babel-helpers": "^6.24.1",
- "babel-messages": "^6.23.0",
- "babel-register": "^6.26.0",
- "babel-runtime": "^6.26.0",
- "babel-template": "^6.26.0",
- "babel-traverse": "^6.26.0",
- "babel-types": "^6.26.0",
- "babylon": "^6.18.0",
- "convert-source-map": "^1.5.1",
- "debug": "^2.6.9",
- "json5": "^0.5.1",
- "lodash": "^4.17.4",
- "minimatch": "^3.0.4",
- "path-is-absolute": "^1.0.1",
- "private": "^0.1.8",
- "slash": "^1.0.0",
- "source-map": "^0.5.7"
+ "find-up": "^3.0.0",
+ "istanbul-lib-instrument": "^3.0.0",
+ "test-exclude": "^5.0.0"
}
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
- }
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+ "dev": true
},
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
- "ms": "2.0.0"
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
}
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"dev": true,
"requires": {
- "is-posix-bracket": "^0.1.0"
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
}
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "ci-info": "^2.0.0"
+ }
+ },
+ "istanbul-lib-instrument": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz",
+ "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "istanbul-lib-coverage": "^2.0.3",
+ "semver": "^5.5.0"
}
},
"jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
+ "@babel/code-frame": "^7.0.0",
"chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
"stack-utils": "^1.0.1"
}
},
"jest-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.4.0.tgz",
- "integrity": "sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
"dev": true,
"requires": {
- "callsites": "^2.0.0",
+ "callsites": "^3.0.0",
"chalk": "^2.0.1",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "jest-message-util": "^23.4.0",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
"mkdirp": "^0.5.1",
- "slash": "^1.0.0",
+ "slash": "^2.0.0",
"source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "invert-kv": "^2.0.0"
}
},
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+ "load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"dev": true,
"requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "mem": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz",
+ "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "^0.1.1",
+ "mimic-fn": "^1.0.0",
+ "p-is-promise": "^2.0.0"
+ }
+ },
+ "os-locale": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "lcid": "^2.0.0",
+ "mem": "^4.0.0"
+ }
+ },
+ "p-is-promise": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz",
+ "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
+ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "read-pkg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
}
},
+ "read-pkg-up": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz",
+ "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0",
+ "read-pkg": "^3.0.0"
+ }
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
+ "test-exclude": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz",
+ "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==",
+ "dev": true,
+ "requires": {
+ "arrify": "^1.0.1",
+ "minimatch": "^3.0.4",
+ "read-pkg-up": "^4.0.0",
+ "require-main-filename": "^1.0.1"
+ }
+ },
+ "write-file-atomic": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz",
+ "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.2"
+ }
+ },
"yargs": {
- "version": "11.1.0",
- "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
- "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
"dev": true,
"requires": {
"cliui": "^4.0.0",
- "decamelize": "^1.1.1",
- "find-up": "^2.1.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
"get-caller-file": "^1.0.1",
- "os-locale": "^2.0.0",
+ "os-locale": "^3.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"which-module": "^2.0.0",
- "y18n": "^3.2.1",
- "yargs-parser": "^9.0.2"
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^11.1.1"
}
},
"yargs-parser": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz",
- "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=",
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
+ "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
"dev": true,
"requires": {
- "camelcase": "^4.1.0"
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
}
}
}
},
"jest-serializer": {
- "version": "23.0.1",
- "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-23.0.1.tgz",
- "integrity": "sha1-o3dq6zEekP6D+rnlM+hRAr0WQWU=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.0.0.tgz",
+ "integrity": "sha512-9FKxQyrFgHtx3ozU+1a8v938ILBE7S8Ko3uiAVjT8Yfi2o91j/fj81jacCQZ/Ihjiff/VsUCXVgQ+iF1XdImOw==",
"dev": true
},
"jest-snapshot": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-23.6.0.tgz",
- "integrity": "sha512-tM7/Bprftun6Cvj2Awh/ikS7zV3pVwjRYU2qNYS51VZHgaAMBs5l4o/69AiDHhQrj5+LA2Lq4VIvK7zYk/bswg==",
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.1.0.tgz",
+ "integrity": "sha512-th6TDfFqEmXvuViacU1ikD7xFb7lQsPn2rJl7OEmnfIVpnrx3QNY2t3PE88meeg0u/mQ0nkyvmC05PBqO4USFA==",
"dev": true,
"requires": {
- "babel-types": "^6.0.0",
+ "@babel/types": "^7.0.0",
"chalk": "^2.0.1",
- "jest-diff": "^23.6.0",
- "jest-matcher-utils": "^23.6.0",
- "jest-message-util": "^23.4.0",
- "jest-resolve": "^23.6.0",
+ "jest-diff": "^24.0.0",
+ "jest-matcher-utils": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-resolve": "^24.1.0",
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
- "pretty-format": "^23.6.0",
+ "pretty-format": "^24.0.0",
"semver": "^5.5.0"
},
"dependencies": {
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "jest-message-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "arr-flatten": "^1.0.1"
+ "@babel/code-frame": "^7.0.0",
+ "chalk": "^2.0.1",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
+ "stack-utils": "^1.0.1"
}
},
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
+ }
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ }
+ }
+ },
+ "jest-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0",
+ "chalk": "^2.0.1",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
+ "mkdirp": "^0.5.1",
+ "slash": "^2.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
"dev": true
},
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "expand-range": "^1.8.1",
- "preserve": "^0.2.0",
- "repeat-element": "^1.1.2"
+ "ci-info": "^2.0.0"
}
},
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "jest-validate": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.0.0.tgz",
+ "integrity": "sha512-vMrKrTOP4BBFIeOWsjpsDgVXATxCspC9S1gqvbJ3Tnn/b9ACsJmteYeVx9830UMV28Cob1RX55x96Qq3Tfad4g==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "chalk": "^2.0.1",
+ "jest-get-type": "^24.0.0",
+ "leven": "^2.1.0",
+ "pretty-format": "^24.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true
+ },
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
"dev": true,
"requires": {
- "is-posix-bracket": "^0.1.0"
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
}
+ }
+ }
+ },
+ "jest-watcher": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.0.0.tgz",
+ "integrity": "sha512-GxkW2QrZ4YxmW1GUWER05McjVDunBlKMFfExu+VsGmXJmpej1saTEKvONdx5RJBlVdpPI5x6E3+EDQSIGgl53g==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.1",
+ "jest-util": "^24.0.0",
+ "string-length": "^2.0.0"
+ },
+ "dependencies": {
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+ "dev": true
},
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
- "is-extglob": "^1.0.0"
+ "ci-info": "^2.0.0"
}
},
"jest-message-util": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz",
- "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
"dev": true,
"requires": {
- "@babel/code-frame": "^7.0.0-beta.35",
+ "@babel/code-frame": "^7.0.0",
"chalk": "^2.0.1",
- "micromatch": "^2.3.11",
- "slash": "^1.0.0",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
"stack-utils": "^1.0.1"
}
},
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "jest-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
"dev": true,
"requires": {
- "is-buffer": "^1.1.5"
+ "callsites": "^3.0.0",
+ "chalk": "^2.0.1",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
+ "mkdirp": "^0.5.1",
+ "slash": "^2.0.0",
+ "source-map": "^0.6.0"
}
},
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
- "dev": true,
- "requires": {
- "arr-diff": "^2.0.0",
- "array-unique": "^0.2.1",
- "braces": "^1.8.2",
- "expand-brackets": "^0.1.4",
- "extglob": "^0.3.1",
- "filename-regex": "^2.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.1",
- "kind-of": "^3.0.2",
- "normalize-path": "^2.0.1",
- "object.omit": "^2.0.0",
- "parse-glob": "^3.0.4",
- "regex-cache": "^0.4.2"
- }
- }
- }
- },
- "jest-util": {
- "version": "22.4.3",
- "resolved": "http://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz",
- "integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==",
- "dev": true,
- "requires": {
- "callsites": "^2.0.0",
- "chalk": "^2.0.1",
- "graceful-fs": "^4.1.11",
- "is-ci": "^1.0.10",
- "jest-message-util": "^22.4.3",
- "mkdirp": "^0.5.1",
- "source-map": "^0.6.0"
- },
- "dependencies": {
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -12815,36 +12746,25 @@
}
}
},
- "jest-validate": {
- "version": "23.6.0",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-23.6.0.tgz",
- "integrity": "sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==",
- "dev": true,
- "requires": {
- "chalk": "^2.0.1",
- "jest-get-type": "^22.1.0",
- "leven": "^2.1.0",
- "pretty-format": "^23.6.0"
- }
- },
- "jest-watcher": {
- "version": "23.4.0",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-23.4.0.tgz",
- "integrity": "sha1-0uKM50+NrWxq/JIrksq+9u0FyRw=",
- "dev": true,
- "requires": {
- "ansi-escapes": "^3.0.0",
- "chalk": "^2.0.1",
- "string-length": "^2.0.0"
- }
- },
"jest-worker": {
- "version": "23.2.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-23.2.0.tgz",
- "integrity": "sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=",
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.0.0.tgz",
+ "integrity": "sha512-s64/OThpfQvoCeHG963MiEZOAAxu8kHsaL/rCMF7lpdzo7vgF0CtPml9hfguOMgykgH/eOm4jFP4ibfHLruytg==",
"dev": true,
"requires": {
- "merge-stream": "^1.0.1"
+ "merge-stream": "^1.0.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
}
},
"js-base64": {
@@ -13046,9 +12966,9 @@
"dev": true
},
"kleur": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-2.0.2.tgz",
- "integrity": "sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.2.tgz",
+ "integrity": "sha512-3h7B2WRT5LNXOtQiAaWonilegHcPSf9nLVXlSTci8lu1dZUuui61+EsPEZqSVxY7rXYmB2DVKMQILxaO5WL61Q==",
"dev": true
},
"lazy-cache": {
@@ -13315,6 +13235,24 @@
"symbol-observable": "^1.1.0"
}
},
+ "jest-get-type": {
+ "version": "22.4.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
+ "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
+ "dev": true
+ },
+ "jest-validate": {
+ "version": "23.6.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-23.6.0.tgz",
+ "integrity": "sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1",
+ "jest-get-type": "^22.1.0",
+ "leven": "^2.1.0",
+ "pretty-format": "^23.6.0"
+ }
+ },
"listr": {
"version": "0.14.3",
"resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
@@ -13520,29 +13458,6 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
- "lodash._baseisequal": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz",
- "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=",
- "dev": true,
- "requires": {
- "lodash.isarray": "^3.0.0",
- "lodash.istypedarray": "^3.0.0",
- "lodash.keys": "^3.0.0"
- }
- },
- "lodash._bindcallback": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
- "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=",
- "dev": true
- },
- "lodash._getnative": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
- "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
- "dev": true
- },
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -13579,45 +13494,12 @@
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
"dev": true
},
- "lodash.isarguments": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
- "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
- "dev": true
- },
- "lodash.isarray": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
- "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
- "dev": true
- },
"lodash.isequal": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-3.0.4.tgz",
- "integrity": "sha1-HDXrO27wzR/1F0Pj6jz3/f/ay2Q=",
- "dev": true,
- "requires": {
- "lodash._baseisequal": "^3.0.0",
- "lodash._bindcallback": "^3.0.0"
- }
- },
- "lodash.istypedarray": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
- "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=",
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
"dev": true
},
- "lodash.keys": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
- "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
- "dev": true,
- "requires": {
- "lodash._getnative": "^3.0.0",
- "lodash.isarguments": "^3.0.0",
- "lodash.isarray": "^3.0.0"
- }
- },
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -13925,12 +13807,6 @@
"integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=",
"dev": true
},
- "map-stream": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
- "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=",
- "dev": true
- },
"map-values": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/map-values/-/map-values-1.0.1.tgz",
@@ -13964,12 +13840,6 @@
"integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=",
"dev": true
},
- "math-random": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
- "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=",
- "dev": true
- },
"mathml-tag-names": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz",
@@ -14628,6 +14498,12 @@
}
}
},
+ "node-modules-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
+ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=",
+ "dev": true
+ },
"node-notifier": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.3.0.tgz",
@@ -15245,6 +15121,18 @@
"has": "^1.0.1"
}
},
+ "object.fromentries": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz",
+ "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.11.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.1"
+ }
+ },
"object.getownpropertydescriptors": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
@@ -15255,16 +15143,6 @@
"es-abstract": "^1.5.1"
}
},
- "object.omit": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
- "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
- "dev": true,
- "requires": {
- "for-own": "^0.1.4",
- "is-extendable": "^0.1.1"
- }
- },
"object.pick": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
@@ -15410,6 +15288,15 @@
"integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
"dev": true
},
+ "p-each-series": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz",
+ "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=",
+ "dev": true,
+ "requires": {
+ "p-reduce": "^1.0.0"
+ }
+ },
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@@ -15734,18 +15621,6 @@
"integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=",
"dev": true
},
- "parse-glob": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
- "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
- "dev": true,
- "requires": {
- "glob-base": "^0.3.0",
- "is-dotfile": "^1.0.0",
- "is-extglob": "^1.0.0",
- "is-glob": "^2.0.0"
- }
- },
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
@@ -15867,15 +15742,6 @@
}
}
},
- "pause-stream": {
- "version": "0.0.11",
- "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
- "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
- "dev": true,
- "requires": {
- "through": "~2.3"
- }
- },
"pbkdf2": {
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
@@ -15933,6 +15799,15 @@
"pinkie": "^2.0.0"
}
},
+ "pirates": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
+ "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==",
+ "dev": true,
+ "requires": {
+ "node-modules-regexp": "^1.0.0"
+ }
+ },
"pkg-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
@@ -17717,12 +17592,6 @@
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
"dev": true
},
- "preserve": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
- "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
- "dev": true
- },
"pretty-format": {
"version": "23.6.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz",
@@ -17782,13 +17651,13 @@
}
},
"prompts": {
- "version": "0.1.14",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-0.1.14.tgz",
- "integrity": "sha512-rxkyiE9YH6zAz/rZpywySLKkpaj0NMVyNw1qhsubdbjjSgcayjTShDreZGlFMcGSu5sab3bAKPfFk78PB90+8w==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.3.tgz",
+ "integrity": "sha512-H8oWEoRZpybm6NV4to9/1limhttEo13xK62pNvn2JzY0MA03p7s0OjtmhXyon3uJmxiJJVSuUwEJFFssI3eBiQ==",
"dev": true,
"requires": {
- "kleur": "^2.0.1",
- "sisteransi": "^0.1.1"
+ "kleur": "^3.0.2",
+ "sisteransi": "^1.0.0"
}
},
"promzard": {
@@ -17862,15 +17731,6 @@
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
- "ps-tree": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
- "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
- "dev": true,
- "requires": {
- "event-stream": "=3.3.4"
- }
- },
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@@ -18003,25 +17863,6 @@
"ret": "~0.1.10"
}
},
- "randomatic": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz",
- "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==",
- "dev": true,
- "requires": {
- "is-number": "^4.0.0",
- "kind-of": "^6.0.0",
- "math-random": "^1.0.1"
- },
- "dependencies": {
- "is-number": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
- "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
- "dev": true
- }
- }
- },
"randombytes": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
@@ -18555,15 +18396,6 @@
"private": "^0.1.6"
}
},
- "regex-cache": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
- "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
- "dev": true,
- "requires": {
- "is-equal-shallow": "^0.1.3"
- }
- },
"regex-not": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@@ -19190,14 +19022,15 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sane": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz",
- "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/sane/-/sane-3.1.0.tgz",
+ "integrity": "sha512-G5GClRRxT1cELXfdAq7UKtUsv8q/ZC5k8lQGmjEm4HcAl3HzBy68iglyNCmw4+0tiXPCBZntslHlRhbnsSws+Q==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
"capture-exit": "^1.2.0",
"exec-sh": "^0.2.0",
+ "execa": "^1.0.0",
"fb-watchman": "^2.0.0",
"fsevents": "^1.2.3",
"micromatch": "^3.1.4",
@@ -19206,11 +19039,58 @@
"watch": "~0.18.0"
},
"dependencies": {
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
}
}
},
@@ -19632,9 +19512,9 @@
}
},
"sisteransi": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz",
- "integrity": "sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz",
+ "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==",
"dev": true
},
"slash": {
@@ -19836,12 +19716,21 @@
}
},
"source-map-support": {
- "version": "0.4.18",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
- "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
+ "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
"dev": true,
"requires": {
- "source-map": "^0.5.6"
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
}
},
"source-map-url": {
@@ -19857,15 +19746,23 @@
"dev": true
},
"spawnd": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-3.5.2.tgz",
- "integrity": "sha512-taf6nYLIl8b3b1RNt0YuxnIUUgHqfx+nix8Rdr2FkNG8259+Jt8YJahrPDShOPa9vCMnDPfPsefRAY/oJy+QBg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-4.0.0.tgz",
+ "integrity": "sha512-ql3qhJnhAkvXpaqKBWOqou1rUTSQhFRaZkyOT+MTFB4xY3X+brgw6LTWV2wHuE9A6YPhrNe1cbg7S+jAYnbC0Q==",
"dev": true,
"requires": {
"exit": "^0.1.2",
"signal-exit": "^3.0.2",
- "terminate": "^2.1.2",
+ "tree-kill": "^1.2.1",
"wait-port": "^0.2.2"
+ },
+ "dependencies": {
+ "tree-kill": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz",
+ "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==",
+ "dev": true
+ }
}
},
"spdx-correct": {
@@ -20040,15 +19937,6 @@
"readable-stream": "^2.0.2"
}
},
- "stream-combiner": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
- "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=",
- "dev": true,
- "requires": {
- "duplexer": "~0.1.1"
- }
- },
"stream-each": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz",
@@ -20828,28 +20716,6 @@
}
}
},
- "terminate": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.1.2.tgz",
- "integrity": "sha512-ltKc9MkgcRe7gzD7XSttHCF1feKM1pTkCdb58jFVWk1efPN9JIk/BHSlOaYF+hCcWoubeJQ8C8Phb0++fa6iNQ==",
- "dev": true,
- "requires": {
- "ps-tree": "^1.1.1"
- }
- },
- "test-exclude": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.1.tgz",
- "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==",
- "dev": true,
- "requires": {
- "arrify": "^1.0.1",
- "micromatch": "^3.1.8",
- "object-assign": "^4.1.0",
- "read-pkg-up": "^1.0.1",
- "require-main-filename": "^1.0.1"
- }
- },
"text-extensions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz",
diff --git a/package.json b/package.json
index 88a28a598a7f7..63f478e6c6fa3 100644
--- a/package.json
+++ b/package.json
@@ -87,7 +87,7 @@
"deasync": "0.1.14",
"deep-freeze": "0.0.1",
"doctrine": "2.1.0",
- "enzyme": "3.7.0",
+ "enzyme": "3.9.0",
"eslint-plugin-jest": "21.5.0",
"espree": "3.5.4",
"fbjs": "0.8.17",
diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md
index dc58eaec5c971..40e47005778e4 100644
--- a/packages/babel-preset-default/CHANGELOG.md
+++ b/packages/babel-preset-default/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 4.0.0 (Unreleased)
+
+## Breaking Change
+
+- Removed `babel-core` dependency acting as Babel 7 bridge ([#13922](https://github.com/WordPress/gutenberg/pull/13922). Ensure all references to `babel-core` are replaced with `@babel/core` .
+
## 3.0.0 (2018-09-30)
## Breaking Change
diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json
index ae77d77c8768b..de20169a475b6 100644
--- a/packages/babel-preset-default/package.json
+++ b/packages/babel-preset-default/package.json
@@ -30,8 +30,7 @@
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@babel/runtime": "^7.3.1",
- "@wordpress/browserslist-config": "file:../browserslist-config",
- "babel-core": "^7.0.0-bridge.0"
+ "@wordpress/browserslist-config": "file:../browserslist-config"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
diff --git a/packages/babel-preset-default/test/index.js b/packages/babel-preset-default/test/index.js
index 7465ce2f73512..0be3197b9541c 100644
--- a/packages/babel-preset-default/test/index.js
+++ b/packages/babel-preset-default/test/index.js
@@ -3,7 +3,7 @@
*/
import path from 'path';
import { readFileSync } from 'fs';
-import { transform } from 'babel-core';
+import { transform } from '@babel/core';
/**
* Internal dependencies
diff --git a/packages/components/src/slot-fill/test/index.js b/packages/components/src/slot-fill/test/index.js
index 0b13790602c87..6bd4c3a7c9247 100644
--- a/packages/components/src/slot-fill/test/index.js
+++ b/packages/components/src/slot-fill/test/index.js
@@ -16,13 +16,13 @@ describe( 'createSlotFill', () => {
const wrapper = shallow( );
expect( wrapper.type() ).toBe( Fill );
- expect( wrapper ).toHaveProp( 'name', SLOT_NAME );
+ expect( wrapper.prop( 'name' ) ).toBe( SLOT_NAME );
} );
test( 'should match snapshot for Slot', () => {
const wrapper = shallow( );
expect( wrapper.type() ).toBe( Slot );
- expect( wrapper ).toHaveProp( 'name', SLOT_NAME );
+ expect( wrapper.prop( 'name' ) ).toBe( SLOT_NAME );
} );
} );
diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json
index a297150615e24..ca3e6b12d913f 100644
--- a/packages/e2e-test-utils/package.json
+++ b/packages/e2e-test-utils/package.json
@@ -30,7 +30,7 @@
"node-fetch": "^1.7.3"
},
"peerDependencies": {
- "jest": ">=23",
+ "jest": ">=24",
"puppeteer": ">=1.6"
},
"publishConfig": {
diff --git a/packages/e2e-tests/config/setup-test-framework.js b/packages/e2e-tests/config/setup-test-framework.js
index 5a28430dda2fb..a3129c4d82c48 100644
--- a/packages/e2e-tests/config/setup-test-framework.js
+++ b/packages/e2e-tests/config/setup-test-framework.js
@@ -1,7 +1,6 @@
/**
* External dependencies
*/
-import 'expect-puppeteer';
import { get } from 'lodash';
/**
diff --git a/packages/e2e-tests/jest.config.js b/packages/e2e-tests/jest.config.js
index f241715197b97..79d6518948624 100644
--- a/packages/e2e-tests/jest.config.js
+++ b/packages/e2e-tests/jest.config.js
@@ -1,7 +1,14 @@
module.exports = {
...require( '@wordpress/scripts/config/jest-e2e.config' ),
- setupTestFrameworkScriptFile: '/config/setup-test-framework.js',
setupFiles: [
'/config/gutenberg-phase.js',
],
+ setupFilesAfterEnv: [
+ '/config/setup-test-framework.js',
+ 'expect-puppeteer',
+ ],
+ transformIgnorePatterns: [
+ 'node_modules',
+ 'scripts/config/puppeteer.config.js',
+ ],
};
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index 9dad36681bc9b..a79a08fb53e16 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -21,11 +21,10 @@
"@wordpress/e2e-test-utils": "file:../e2e-test-utils",
"@wordpress/jest-console": "file:../jest-console",
"@wordpress/scripts": "file:../scripts",
- "expect-puppeteer": "^3.2.0",
"lodash": "^4.17.11"
},
"peerDependencies": {
- "jest": ">=23",
+ "jest": ">=24",
"puppeteer": ">=1.6"
},
"publishConfig": {
diff --git a/packages/editor/src/components/block-edit/test/edit.js b/packages/editor/src/components/block-edit/test/edit.js
index 0f795fdd72503..d09ce608cc14b 100644
--- a/packages/editor/src/components/block-edit/test/edit.js
+++ b/packages/editor/src/components/block-edit/test/edit.js
@@ -42,7 +42,7 @@ describe( 'Edit', () => {
const wrapper = shallow( );
- expect( wrapper.find( edit ) ).toExist();
+ expect( wrapper.exists( edit ) ).toBe( true );
} );
it( 'should use save implementation of block as fallback', () => {
@@ -55,7 +55,7 @@ describe( 'Edit', () => {
const wrapper = shallow( );
- expect( wrapper.find( save ) ).toExist();
+ expect( wrapper.exists( save ) ).toBe( true );
} );
it( 'should combine the default class name with a custom one', () => {
@@ -74,6 +74,7 @@ describe( 'Edit', () => {
);
- expect( wrapper.find( edit ) ).toHaveClassName( 'wp-block-test-block my-class' );
+ expect( wrapper.find( edit ).hasClass( 'wp-block-test-block' ) ).toBe( true );
+ expect( wrapper.find( edit ).hasClass( 'my-class' ) ).toBe( true );
} );
} );
diff --git a/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap b/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap
index c542d5b5dd65e..9d6198255a377 100644
--- a/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap
@@ -16,7 +16,12 @@ exports[`DefaultBlockAppender should append a default block when input focused 1
"calls": Array [
Array [],
],
- "results": undefined,
+ "results": Array [
+ Object {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
}
}
readOnly={true}
diff --git a/packages/editor/src/components/page-attributes/test/check.js b/packages/editor/src/components/page-attributes/test/check.js
index 3faa6405ca9d9..9519d0540b78a 100644
--- a/packages/editor/src/components/page-attributes/test/check.js
+++ b/packages/editor/src/components/page-attributes/test/check.js
@@ -40,18 +40,18 @@ describe( 'PageAttributesCheck', () => {
it( 'should render if page attributes support is true and no available templates exist', () => {
const wrapper = shallow( content );
- expect( wrapper ).toHaveText( 'content' );
+ expect( wrapper.text() ).toContain( 'content' );
} );
it( 'should render if page attributes support is false/unknown and available templates exist', () => {
const wrapper = shallow( content );
- expect( wrapper ).toHaveText( 'content' );
+ expect( wrapper.text() ).toContain( 'content' );
} );
it( 'should render if page attributes support is true and available templates exist', () => {
const wrapper = shallow( content );
- expect( wrapper ).toHaveText( 'content' );
+ expect( wrapper.text() ).toContain( 'content' );
} );
} );
diff --git a/packages/jest-console/CHANGELOG.md b/packages/jest-console/CHANGELOG.md
index 040d1ce478d1f..3561c61387c0e 100644
--- a/packages/jest-console/CHANGELOG.md
+++ b/packages/jest-console/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 3.0.0 (Unreleased)
+
+### Breaking Changes
+
+- Increased the recommended Jest dependency to version 24 ([#13922](https://github.com/WordPress/gutenberg/pull/13922).
+
## 2.0.7 (2018-11-20)
## 2.0.5 (2018-09-30)
@@ -8,7 +14,7 @@
## 2.0.0 (2018-07-12)
-### Breaking Change
+### Breaking Changes
- Add new API methods `toHaveInformed`, `toHaveInformedWith`, `toHaveLogged` and `toHaveLoggedWith` ([#137](https://github.com/WordPress/packages/pull/137)). If the code under test calls `console.log` or `console.info` it will fail, unless one of the newly introduced methods is explicitly used to verify it.
- Updated code to work with Babel 7 ([#7832](https://github.com/WordPress/gutenberg/pull/7832))
diff --git a/packages/jest-console/README.md b/packages/jest-console/README.md
index 8ba0a5346a7ef..31909d570122a 100644
--- a/packages/jest-console/README.md
+++ b/packages/jest-console/README.md
@@ -20,20 +20,16 @@ npm install @wordpress/jest-console --save-dev
### Setup
-The simplest setup is to use Jest's `setupTestFrameworkScriptFile` config option:
+The simplest setup is to use Jest's `setupFilesAfterEnv` config option:
```js
"jest": {
- "setupTestFrameworkScriptFile": "./node_modules/@wordpress/jest-console/build/index.js"
+ "setupFilesAfterEnv": [
+ "@wordpress/jest-console"
+ ]
},
```
-If your project already has a script file which sets up the test framework, you will need the following import statement:
-
-```js
-import '@wordpress/jest-console';
-```
-
### Usage
### `.toHaveErrored()`
diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json
index 424b590cf64ad..ae0f3902fa50f 100644
--- a/packages/jest-console/package.json
+++ b/packages/jest-console/package.json
@@ -26,11 +26,11 @@
"module": "build-module/index.js",
"dependencies": {
"@babel/runtime": "^7.3.1",
- "jest-matcher-utils": "^23.6.0",
+ "jest-matcher-utils": "^24.0.0",
"lodash": "^4.17.11"
},
"peerDependencies": {
- "jest": ">=22"
+ "jest": ">=24"
},
"publishConfig": {
"access": "public"
diff --git a/packages/jest-preset-default/CHANGELOG.md b/packages/jest-preset-default/CHANGELOG.md
index d8e7f82455649..7ff9fd2ced551 100644
--- a/packages/jest-preset-default/CHANGELOG.md
+++ b/packages/jest-preset-default/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 4.0.0 (Unreleased)
+
+### Breaking Changes
+
+- The bundled `jest` dependency has been updated from requiring `^23.6.0` to requiring `^24.1.0` (see [Breaking Changes](https://jestjs.io/blog/2019/01/25/jest-24-refreshing-polished-typescript-friendly#breaking-changes), [#13922](https://github.com/WordPress/gutenberg/pull/13922)).
+- The bundled `jest-enzyme` dependency has been removed completely ([#13922](https://github.com/WordPress/gutenberg/pull/13922)).
+
+### Internal
+
+- The bundled `enzyme` dependency has been updated from requiring `^3.7.0` to requiring `^3.9.0` ([#13922](https://github.com/WordPress/gutenberg/pull/13922)).
+
## 3.0.3 (2018-11-20)
## 3.0.2 (2018-11-09)
diff --git a/packages/jest-preset-default/README.md b/packages/jest-preset-default/README.md
index 059bdfc9a8859..52e7df8c715fd 100644
--- a/packages/jest-preset-default/README.md
+++ b/packages/jest-preset-default/README.md
@@ -27,21 +27,11 @@ npm install @wordpress/jest-preset-default --save-dev
* `moduleNameMapper` - all `css` and `scss` files containing CSS styles will be stubbed out.
* `modulePaths` - the root dir of the project is used as a location to search when resolving modules.
* `setupFiles` - runs code before each test which sets up global variables required in the testing environment.
-* `setupTestFrameworkScriptFile` - runs code which adds improved support for `Console` object and `React` components to the testing framework before each test.
+* `setupFilesAfterEnv` - runs code which adds improved support for `Console` object and `React` components to the testing framework before each test.
+* `snapshotSerializers` - makes it possible to use snapshot tests on `Enzyme` wrappers.
* `testMatch`- includes `/test/` subfolder in the glob patterns Jest uses to detect test files. It detects only test files containing `.js` extension.
* `timers` - use of [fake timers](https://jestjs.io/docs/en/timer-mocks.html) for functions such as `setTimeout` is enabled.
-* `transform` - adds support for [PEG.js]( https://github.com/pegjs/pegjs#javascript-api) transformed necessary for WordPress blocks. It also keeps the default [babel-jest](https://github.com/facebook/jest/tree/master/packages/babel-jest) transformer.
+* `transform` - keeps the default [babel-jest](https://github.com/facebook/jest/tree/master/packages/babel-jest) transformer.
* `verbose` - each individual test won't be reported during the run.
-#### Overriding `setupTestFrameworkScriptFile`
-
-It is also possible to override the script that runs some code to configure or set up the testing framework before each test. To do so you will need to create `setup-test-framework.js` inside your project with the following content:
-
-```js
-// You can still load the default script provided by this setup
-import '@wordpress/jest-preset-default';
-
-// Your code goes here
-```
-
diff --git a/packages/jest-preset-default/jest-preset.json b/packages/jest-preset-default/jest-preset.json
index 3995497bfb5ba..be09fe7d6bb57 100644
--- a/packages/jest-preset-default/jest-preset.json
+++ b/packages/jest-preset-default/jest-preset.json
@@ -8,7 +8,12 @@
"setupFiles": [
"/node_modules/@wordpress/jest-preset-default/scripts/setup-globals.js"
],
- "setupTestFrameworkScriptFile": "/node_modules/@wordpress/jest-preset-default/scripts/setup-test-framework.js",
+ "setupFilesAfterEnv": [
+ "/node_modules/@wordpress/jest-preset-default/scripts/setup-test-framework.js"
+ ],
+ "snapshotSerializers": [
+ "/node_modules/enzyme-to-json/serializer.js"
+ ],
"testMatch": [
"**/__tests__/**/*.js",
"**/?(*.)(spec|test).js",
@@ -16,7 +21,7 @@
],
"timers": "fake",
"transform": {
- "^.+\\.jsx?$": "/node_modules/babel-jest"
+ "^.+\\.[jt]sx?$": "/node_modules/babel-jest"
},
"verbose": true
}
diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json
index 29eb38c9cb823..fbabe4c53bc69 100644
--- a/packages/jest-preset-default/package.json
+++ b/packages/jest-preset-default/package.json
@@ -27,13 +27,13 @@
"main": "index.js",
"dependencies": {
"@wordpress/jest-console": "file:../jest-console",
- "babel-jest": "^23.6.0",
- "enzyme": "^3.7.0",
- "enzyme-adapter-react-16": "^1.6.0",
- "jest-enzyme": "^6.0.2"
+ "babel-jest": "^24.1.0",
+ "enzyme": "^3.9.0",
+ "enzyme-adapter-react-16": "^1.9.1",
+ "enzyme-to-json": "^3.3.5"
},
"peerDependencies": {
- "jest": ">=23"
+ "jest": ">=24"
},
"publishConfig": {
"access": "public"
diff --git a/packages/jest-preset-default/scripts/setup-test-framework.js b/packages/jest-preset-default/scripts/setup-test-framework.js
index f2374bfd4f4f7..9c026a633ff46 100644
--- a/packages/jest-preset-default/scripts/setup-test-framework.js
+++ b/packages/jest-preset-default/scripts/setup-test-framework.js
@@ -14,9 +14,6 @@ jest.mock( 'enzyme', () => {
// configure enzyme 3 for React, from docs: http://airbnb.io/enzyme/docs/installation/index.html
const Adapter = require.requireActual( 'enzyme-adapter-react-16' );
actualEnzyme.configure( { adapter: new Adapter() } );
-
- // configure assertions for enzyme
- require.requireActual( 'jest-enzyme' );
}
return actualEnzyme;
} );
diff --git a/packages/jest-puppeteer-axe/README.md b/packages/jest-puppeteer-axe/README.md
index fc85725e27e9a..29fd06538ed47 100644
--- a/packages/jest-puppeteer-axe/README.md
+++ b/packages/jest-puppeteer-axe/README.md
@@ -14,20 +14,16 @@ npm install @wordpress/jest-puppeteer-axe --save-dev
### Setup
-The simplest setup is to use Jest's `setupTestFrameworkScriptFile` config option:
+The simplest setup is to use Jest's `setupFilesAfterEnv` config option:
```js
"jest": {
- "setupTestFrameworkScriptFile": "./node_modules/@wordpress/jest-puppeteer-axe/build/index.js"
+ "setupFilesAfterEnv": [
+ "@wordpress/jest-puppeteer-axe"
+ ]
},
```
-If your project already has a script file which sets up the test framework, you will need the following import statement:
-
-```js
-import '@wordpress/jest-puppeteer-axe';
-```
-
## Usage
In your Jest test suite add the following code to the test's body:
diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json
index 5006bbf5344a1..2c73930c9bca0 100644
--- a/packages/jest-puppeteer-axe/package.json
+++ b/packages/jest-puppeteer-axe/package.json
@@ -26,10 +26,10 @@
"main": "build/index.js",
"module": "build-module/index.js",
"dependencies": {
- "axe-puppeteer": "^0.1.0"
+ "axe-puppeteer": "^1.0.0"
},
"peerDependencies": {
- "jest": ">=23",
+ "jest": ">=24",
"puppeteer": ">=1.6"
},
"publishConfig": {
diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md
index fe24a86900ee0..342ada51d8374 100644
--- a/packages/scripts/CHANGELOG.md
+++ b/packages/scripts/CHANGELOG.md
@@ -3,6 +3,8 @@
### Breaking Changes
- The bundled `eslint` dependency has been updated from requiring `^4.19.1` to requiring `^5.12.1` (see [Migration Guide](https://eslint.org/docs/user-guide/migrating-to-5.0.0)).
+- The bundled `jest` dependency has been updated from requiring `^23.6.0` to requiring `^24.1.0` (see [Breaking Changes](https://jestjs.io/blog/2019/01/25/jest-24-refreshing-polished-typescript-friendly#breaking-changes), [#13922](https://github.com/WordPress/gutenberg/pull/13922)).
+- The bundled `jest-puppeteer` dependency has been updated from requiring `3.2.1` to requiring `^4.0.0` ([#13922](https://github.com/WordPress/gutenberg/pull/13922)).
### New Features
diff --git a/packages/scripts/config/jest-e2e.config.js b/packages/scripts/config/jest-e2e.config.js
index fd8d722b8c3fc..d27b8d1b8cd2b 100644
--- a/packages/scripts/config/jest-e2e.config.js
+++ b/packages/scripts/config/jest-e2e.config.js
@@ -19,7 +19,7 @@ const jestE2EConfig = {
if ( ! hasBabelConfig() ) {
jestE2EConfig.transform = {
- '^.+\\.jsx?$': path.join( __dirname, 'babel-transform' ),
+ '^.+\\.[jt]sx?$': path.join( __dirname, 'babel-transform' ),
};
}
diff --git a/packages/scripts/config/jest-unit.config.js b/packages/scripts/config/jest-unit.config.js
index 34f48ea0e3376..3244e163925d6 100644
--- a/packages/scripts/config/jest-unit.config.js
+++ b/packages/scripts/config/jest-unit.config.js
@@ -14,7 +14,7 @@ const jestUnitConfig = {
if ( ! hasBabelConfig() ) {
jestUnitConfig.transform = {
- '^.+\\.jsx?$': path.join( __dirname, 'babel-transform' ),
+ '^.+\\.[jt]sx?$': path.join( __dirname, 'babel-transform' ),
};
}
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 388bc0a26a3b8..3a1c92123c8b1 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -40,8 +40,8 @@
"check-node-version": "^3.1.1",
"cross-spawn": "^5.1.0",
"eslint": "^5.12.1",
- "jest": "^23.6.0",
- "jest-puppeteer": "3.2.1",
+ "jest": "^24.1.0",
+ "jest-puppeteer": "^4.0.0",
"npm-package-json-lint": "^3.3.1",
"puppeteer": "1.6.1",
"read-pkg-up": "^1.0.1",
From 8ea75bf7bf0626260ad4569dd6c99a6e1a177782 Mon Sep 17 00:00:00 2001
From: Chris Van Patten
Date: Sun, 24 Feb 2019 10:49:57 -0500
Subject: [PATCH 019/169] Codeowners changes for @chrisvanpatten (#14062)
* Codeowners changes for @chrisvanpatten
* Remove obsolete ghost placeholder for no longer onowned styles
---
.github/CODEOWNERS | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index dcd01eefb79cc..945b03b7b08d8 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -40,12 +40,12 @@
/packages/scripts @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @nosolosw @mkaz
# UI Components
-/packages/components @youknowriad @gziolo @aduth @chrisvanpatten @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
-/packages/compose @youknowriad @gziolo @aduth @chrisvanpatten @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
-/packages/element @youknowriad @gziolo @aduth @chrisvanpatten @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
-/packages/notices @youknowriad @gziolo @aduth @chrisvanpatten @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
-/packages/nux @youknowriad @gziolo @aduth @chrisvanpatten @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
-/packages/viewport @youknowriad @gziolo @aduth @chrisvanpatten @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
+/packages/components @youknowriad @gziolo @aduth @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks @chrisvanpatten
+/packages/compose @youknowriad @gziolo @aduth @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
+/packages/element @youknowriad @gziolo @aduth @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
+/packages/notices @youknowriad @gziolo @aduth @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
+/packages/nux @youknowriad @gziolo @aduth @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
+/packages/viewport @youknowriad @gziolo @aduth @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks
# Utilities
/packages/a11y @youknowriad @gziolo @aduth
@@ -79,8 +79,8 @@
# Documentation
/docs @youknowriad @gziolo @chrisvanpatten @mkaz @ajitbohra @nosolosw @notnownikki
-# Styles (Unowned)
-*.scss @ghost
+# Styles
+*.scss @chrisvanpatten
# Native (Unowned)
*.native.js @ghost
From 3397ae408a870ded1516fcea77b63cdafb72f873 Mon Sep 17 00:00:00 2001
From: Ned Zimmerman
Date: Sun, 24 Feb 2019 11:52:13 -0400
Subject: [PATCH 020/169] Add repository.directory fields (fixes #13946)
(#14059)
---
packages/a11y/package.json | 3 ++-
packages/annotations/package.json | 3 ++-
packages/api-fetch/package.json | 3 ++-
packages/autop/package.json | 3 ++-
packages/babel-plugin-import-jsx-pragma/package.json | 3 ++-
packages/babel-plugin-makepot/package.json | 3 ++-
packages/babel-preset-default/package.json | 3 ++-
packages/blob/package.json | 3 ++-
packages/block-editor/package.json | 3 ++-
packages/block-library/package.json | 3 ++-
packages/block-serialization-default-parser/package.json | 3 ++-
packages/block-serialization-spec-parser/package.json | 3 ++-
packages/blocks/package.json | 3 ++-
packages/browserslist-config/package.json | 3 ++-
packages/components/package.json | 3 ++-
packages/compose/package.json | 3 ++-
packages/core-data/package.json | 3 ++-
packages/custom-templated-path-webpack-plugin/package.json | 3 ++-
packages/data/package.json | 3 ++-
packages/date/package.json | 3 ++-
packages/deprecated/package.json | 3 ++-
packages/dom-ready/package.json | 3 ++-
packages/dom/package.json | 3 ++-
packages/e2e-test-utils/package.json | 3 ++-
packages/e2e-tests/package.json | 3 ++-
packages/edit-post/package.json | 3 ++-
packages/edit-widgets/package.json | 3 ++-
packages/editor/package.json | 3 ++-
packages/element/package.json | 3 ++-
packages/escape-html/package.json | 3 ++-
packages/eslint-plugin/package.json | 3 ++-
packages/format-library/package.json | 3 ++-
packages/hooks/package.json | 3 ++-
packages/html-entities/package.json | 3 ++-
packages/i18n/package.json | 3 ++-
packages/is-shallow-equal/package.json | 3 ++-
packages/jest-console/package.json | 3 ++-
packages/jest-preset-default/package.json | 3 ++-
packages/jest-puppeteer-axe/package.json | 3 ++-
packages/keycodes/package.json | 3 ++-
packages/library-export-default-webpack-plugin/package.json | 3 ++-
packages/list-reusable-blocks/package.json | 3 ++-
packages/notices/package.json | 3 ++-
packages/npm-package-json-lint-config/package.json | 3 ++-
packages/nux/package.json | 3 ++-
packages/plugins/package.json | 3 ++-
packages/postcss-themes/package.json | 3 ++-
packages/priority-queue/package.json | 3 ++-
packages/redux-routine/package.json | 3 ++-
packages/rich-text/package.json | 3 ++-
packages/scripts/package.json | 3 ++-
packages/shortcode/package.json | 3 ++-
packages/token-list/package.json | 3 ++-
packages/url/package.json | 3 ++-
packages/viewport/package.json | 3 ++-
packages/wordcount/package.json | 3 ++-
56 files changed, 112 insertions(+), 56 deletions(-)
diff --git a/packages/a11y/package.json b/packages/a11y/package.json
index 975ab31b994b3..dac861cbc1869 100644
--- a/packages/a11y/package.json
+++ b/packages/a11y/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/a11y/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/a11y"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/annotations/package.json b/packages/annotations/package.json
index ebbc0b39ca84f..af891a4376c52 100644
--- a/packages/annotations/package.json
+++ b/packages/annotations/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/annotations/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/annotations"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json
index 8f9b6568013a7..d5d4b55da93f7 100644
--- a/packages/api-fetch/package.json
+++ b/packages/api-fetch/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/api-fetch/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/api-fetch"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/autop/package.json b/packages/autop/package.json
index 9735d5475aac5..b89416ed15b2f 100644
--- a/packages/autop/package.json
+++ b/packages/autop/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/autop/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/autop"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json
index 7dac8d0786bfb..3f9efdc87ffa1 100644
--- a/packages/babel-plugin-import-jsx-pragma/package.json
+++ b/packages/babel-plugin-import-jsx-pragma/package.json
@@ -14,7 +14,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/babel-plugin-import-jsx-pragma/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/babel-plugin-import-jsx-pragma"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json
index 1dfd50b4c9984..fae36a7f7ac3d 100644
--- a/packages/babel-plugin-makepot/package.json
+++ b/packages/babel-plugin-makepot/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/babel-plugin-makepot/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/babel-plugin-makepot"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json
index de20169a475b6..a1f2f02e0ac88 100644
--- a/packages/babel-preset-default/package.json
+++ b/packages/babel-preset-default/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/babel-preset-default/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/babel-preset-default"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/blob/package.json b/packages/blob/package.json
index f8c08ee648276..39bdfcf6377b2 100644
--- a/packages/blob/package.json
+++ b/packages/blob/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/blob/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/blob"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index d4f3cf7f757c7..6f593a1063edf 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/block-editor/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/block-editor"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index 431108dd29f3a..9b5d871edf42f 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/block-library/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/block-library"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json
index d698187f122ec..28e9bf0f6c54b 100644
--- a/packages/block-serialization-default-parser/package.json
+++ b/packages/block-serialization-default-parser/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/block-serialization-default-parser/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/block-serialization-default-parser"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json
index 5d40a8cfd03de..335d245be2b9a 100644
--- a/packages/block-serialization-spec-parser/package.json
+++ b/packages/block-serialization-spec-parser/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/block-serialization-spec-parser/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/block-serialization-spec-parser"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/blocks/package.json b/packages/blocks/package.json
index 077d6f5a228c9..eea81e9985e50 100644
--- a/packages/blocks/package.json
+++ b/packages/blocks/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/blocks/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/blocks"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json
index 81baf916c0726..84c51f2345659 100644
--- a/packages/browserslist-config/package.json
+++ b/packages/browserslist-config/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/browserslist-config/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/browserslist-config"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/components/package.json b/packages/components/package.json
index 963e7b1e3545e..9613e850a722d 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/components/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/components"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/compose/package.json b/packages/compose/package.json
index c02e9d263c2e2..875dbf0852d35 100644
--- a/packages/compose/package.json
+++ b/packages/compose/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/compose/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/compose"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/core-data/package.json b/packages/core-data/package.json
index e9e2bb3c97a48..b2173586318f5 100644
--- a/packages/core-data/package.json
+++ b/packages/core-data/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/core-data/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/core-data"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/custom-templated-path-webpack-plugin/package.json b/packages/custom-templated-path-webpack-plugin/package.json
index 4f41dc79fb064..49ca17003c257 100644
--- a/packages/custom-templated-path-webpack-plugin/package.json
+++ b/packages/custom-templated-path-webpack-plugin/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/blob/master/packages/custom-templated-path-webpack-plugin/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/custom-templated-path-webpack-plugin"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/data/package.json b/packages/data/package.json
index 96264a852eadd..c351248f049a9 100644
--- a/packages/data/package.json
+++ b/packages/data/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/data/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/data"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/date/package.json b/packages/date/package.json
index 3aa2437a296bd..becb695d02ebd 100644
--- a/packages/date/package.json
+++ b/packages/date/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/date/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/date"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json
index 6d0dccf40317b..b8584a7e4f266 100644
--- a/packages/deprecated/package.json
+++ b/packages/deprecated/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/deprecated/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/deprecated"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json
index 2c377f2957314..22f70e8341189 100644
--- a/packages/dom-ready/package.json
+++ b/packages/dom-ready/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/dom-ready/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/dom-ready"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/dom/package.json b/packages/dom/package.json
index 6e94a8fc0dae5..75289e1e27ce4 100644
--- a/packages/dom/package.json
+++ b/packages/dom/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/dom/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/dom"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json
index ca3e6b12d913f..071541ed4449c 100644
--- a/packages/e2e-test-utils/package.json
+++ b/packages/e2e-test-utils/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/e2e-test-utils/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/e2e-test-utils"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index a79a08fb53e16..881a497511505 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/e2e-tests/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/e2e-tests"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json
index 88a7818e2fac8..0327ee5121f38 100644
--- a/packages/edit-post/package.json
+++ b/packages/edit-post/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/edit-post/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/edit-post"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json
index a308d8cd6035f..3adbdad1f19a1 100644
--- a/packages/edit-widgets/package.json
+++ b/packages/edit-widgets/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/edit-widgets/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/edit-widgets"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/editor/package.json b/packages/editor/package.json
index acbb00bec5532..8893dbf4fea7c 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/editor/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/editor"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/element/package.json b/packages/element/package.json
index 24df06e6702af..ce76fa344232c 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/element/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/element"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json
index 6fb68c88808ec..5e60de55ab031 100644
--- a/packages/escape-html/package.json
+++ b/packages/escape-html/package.json
@@ -10,7 +10,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/escape-html/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/escape-html"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 125acdac5189e..f115d81c5c41e 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/eslint-plugin/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/eslint-plugin"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/format-library/package.json b/packages/format-library/package.json
index d8fd5b4b7f366..ec4e8a02a90be 100644
--- a/packages/format-library/package.json
+++ b/packages/format-library/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/format-library/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/format-library"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/hooks/package.json b/packages/hooks/package.json
index b4868eb1f4ed6..3469fc00f451e 100644
--- a/packages/hooks/package.json
+++ b/packages/hooks/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/hooks/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/hooks"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json
index 002fd8b8d4a4e..06c7a9d25d59c 100644
--- a/packages/html-entities/package.json
+++ b/packages/html-entities/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/html-entities/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/html-entities"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index e05c99de5c587..91e9e20fd3ea0 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/i18n/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/i18n"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json
index d293276b1e946..06c38ebf63cd2 100644
--- a/packages/is-shallow-equal/package.json
+++ b/packages/is-shallow-equal/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/is-shallow-equal/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/is-shallow-equal"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json
index ae0f3902fa50f..d39dbe4b23026 100644
--- a/packages/jest-console/package.json
+++ b/packages/jest-console/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/jest-console/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/jest-console"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json
index fbabe4c53bc69..42110a066fe89 100644
--- a/packages/jest-preset-default/package.json
+++ b/packages/jest-preset-default/package.json
@@ -15,7 +15,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/jest-preset-default/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/jest-preset-default"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json
index 2c73930c9bca0..6c5e7ba6bc88d 100644
--- a/packages/jest-puppeteer-axe/package.json
+++ b/packages/jest-puppeteer-axe/package.json
@@ -14,7 +14,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/jest-puppeteer-axe/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/jest-puppeteer-axe"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json
index 12a8c50895e66..d062bf6f75a36 100644
--- a/packages/keycodes/package.json
+++ b/packages/keycodes/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/keycodes/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/keycodes"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/library-export-default-webpack-plugin/package.json b/packages/library-export-default-webpack-plugin/package.json
index 7222df5a7b4ff..e2a2881f72902 100644
--- a/packages/library-export-default-webpack-plugin/package.json
+++ b/packages/library-export-default-webpack-plugin/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/library-export-default-webpack-plugin/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/library-export-default-webpack-plugin"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json
index ca1bcefadedc6..9501833c972fb 100644
--- a/packages/list-reusable-blocks/package.json
+++ b/packages/list-reusable-blocks/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/list-reusable-blocks/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/list-reusable-blocks"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/notices/package.json b/packages/notices/package.json
index aacbd324448f1..93f5e16eac4b1 100644
--- a/packages/notices/package.json
+++ b/packages/notices/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/notices/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/notices"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json
index 6fdb45fd6c612..f2dabfdf29af2 100644
--- a/packages/npm-package-json-lint-config/package.json
+++ b/packages/npm-package-json-lint-config/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/npm-package-json-lint-config/README.md",
"repository": {
"type": "git",
- "url": "git+https://github.com/WordPress/gutenberg.git"
+ "url": "git+https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/npm-package-json-lint-config"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/nux/package.json b/packages/nux/package.json
index 0476ec4e77a0f..1b9ebbe5f7e4d 100644
--- a/packages/nux/package.json
+++ b/packages/nux/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/nux/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/nux"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/plugins/package.json b/packages/plugins/package.json
index 1050d8aa78554..5f433e110f6b8 100644
--- a/packages/plugins/package.json
+++ b/packages/plugins/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/plugins/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/plugins"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json
index 0836e71badf9a..cafbbdf93e6b4 100644
--- a/packages/postcss-themes/package.json
+++ b/packages/postcss-themes/package.json
@@ -15,7 +15,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/postcss-themes/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/postcss-themes"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json
index cf921b4739cef..afa5df6e857ec 100644
--- a/packages/priority-queue/package.json
+++ b/packages/priority-queue/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/priority-queue/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/priority-queue"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json
index c1a73f09f8f92..3ee1016b84b68 100644
--- a/packages/redux-routine/package.json
+++ b/packages/redux-routine/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/redux-routine/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/redux-routine"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json
index cfae343520b6e..029e2056d3a5c 100644
--- a/packages/rich-text/package.json
+++ b/packages/rich-text/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/rich-text/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/rich-text"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 3a1c92123c8b1..6a2f625c182ab 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -13,7 +13,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/scripts/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/scripts"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json
index f110b0c3542ef..5fd63da90e79e 100644
--- a/packages/shortcode/package.json
+++ b/packages/shortcode/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/shortcode/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/shortcode"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/token-list/package.json b/packages/token-list/package.json
index 57b74a9c092e8..5c04a6bcb92f3 100644
--- a/packages/token-list/package.json
+++ b/packages/token-list/package.json
@@ -10,7 +10,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/token-list/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/token-list"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/url/package.json b/packages/url/package.json
index 6257dbc7c8674..2ae9d56532c18 100644
--- a/packages/url/package.json
+++ b/packages/url/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/url/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/url"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/viewport/package.json b/packages/viewport/package.json
index 5c38011385207..106bd96b57f3b 100644
--- a/packages/viewport/package.json
+++ b/packages/viewport/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/viewport/README.md",
"repository": {
"type": "git",
- "url": "https://github.com/WordPress/gutenberg.git"
+ "url": "https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/viewport"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json
index c31b0009aad30..a5ec368498b1e 100644
--- a/packages/wordcount/package.json
+++ b/packages/wordcount/package.json
@@ -11,7 +11,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/wordcount/README.md",
"repository": {
"type": "git",
- "url": "git+https://github.com/WordPress/gutenberg.git"
+ "url": "git+https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/wordcount"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
From b0aa8929c887fba9e47263fc8ba901d92cabeea1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?=
Date: Mon, 25 Feb 2019 10:50:41 +0100
Subject: [PATCH 021/169] Improve default Babel preset to include JSX pragma
(#13540)
* Include custome JSX pragma support in Babel preset
* Stop using Babel tranpiliation for two packages
babel-plugin-import-jsx-pragma and postcss-themes
* Add engines field to node based packages
---
babel.config.js | 10 -----
bin/packages/get-babel-config.js | 13 ++-----
bin/packages/get-packages.js | 38 +------------------
package-lock.json | 7 +---
package.json | 4 +-
.../CHANGELOG.md | 1 +
.../{src => }/index.js | 9 ++---
.../package.json | 12 +++---
.../test/index.js | 2 +-
packages/babel-plugin-makepot/package.json | 3 ++
packages/babel-preset-default/CHANGELOG.md | 1 +
packages/babel-preset-default/index.js | 8 ++++
packages/babel-preset-default/package.json | 4 ++
.../package.json | 3 ++
packages/e2e-test-utils/package.json | 3 ++
packages/e2e-tests/package.json | 3 ++
packages/jest-console/package.json | 3 ++
packages/jest-preset-default/package.json | 3 ++
packages/jest-puppeteer-axe/package.json | 3 ++
.../package.json | 3 ++
.../npm-package-json-lint-config/package.json | 3 ++
packages/postcss-themes/CHANGELOG.md | 5 +++
packages/postcss-themes/{src => }/index.js | 3 ++
packages/postcss-themes/package.json | 9 +++--
packages/postcss-themes/test/index.js | 2 +-
25 files changed, 73 insertions(+), 82 deletions(-)
rename packages/babel-plugin-import-jsx-pragma/{src => }/index.js (96%)
create mode 100644 packages/postcss-themes/CHANGELOG.md
rename packages/postcss-themes/{src => }/index.js (98%)
diff --git a/babel.config.js b/babel.config.js
index 6a903eff6c1d9..4dc16df8337b2 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -3,16 +3,6 @@ module.exports = function( api ) {
return {
presets: [ '@wordpress/babel-preset-default' ],
- plugins: [
- [
- '@wordpress/babel-plugin-import-jsx-pragma',
- {
- scopeVariable: 'createElement',
- source: '@wordpress/element',
- isDefault: false,
- },
- ],
- ],
env: {
production: {
plugins: [
diff --git a/bin/packages/get-babel-config.js b/bin/packages/get-babel-config.js
index e79bc306d07c4..b2646da46955c 100644
--- a/bin/packages/get-babel-config.js
+++ b/bin/packages/get-babel-config.js
@@ -10,14 +10,7 @@ const babel = require( '@babel/core' );
const { options: babelDefaultConfig } = babel.loadPartialConfig( {
configFile: '@wordpress/babel-preset-default',
} );
-const plugins = babelDefaultConfig.plugins;
-if ( ! process.env.SKIP_JSX_PRAGMA_TRANSFORM ) {
- plugins.push( [ '@wordpress/babel-plugin-import-jsx-pragma', {
- scopeVariable: 'createElement',
- source: '@wordpress/element',
- isDefault: false,
- } ] );
-}
+const { plugins, presets } = babelDefaultConfig;
const overrideOptions = ( target, targetName, options ) => {
if ( get( target, [ 'file', 'request' ] ) === targetName ) {
@@ -37,7 +30,7 @@ const babelConfigs = {
{
plugins,
presets: map(
- babelDefaultConfig.presets,
+ presets,
( preset ) => overrideOptions( preset, '@babel/preset-env', {
modules: 'commonjs',
} )
@@ -55,7 +48,7 @@ const babelConfigs = {
} )
),
presets: map(
- babelDefaultConfig.presets,
+ presets,
( preset ) => overrideOptions( preset, '@babel/preset-env', {
modules: false,
} )
diff --git a/bin/packages/get-packages.js b/bin/packages/get-packages.js
index 30093a22abba6..ed271db0434f2 100644
--- a/bin/packages/get-packages.js
+++ b/bin/packages/get-packages.js
@@ -3,7 +3,7 @@
*/
const fs = require( 'fs' );
const path = require( 'path' );
-const { overEvery, compact, includes, negate } = require( 'lodash' );
+const { overEvery } = require( 'lodash' );
/**
* Absolute path to packages directory.
@@ -12,36 +12,6 @@ const { overEvery, compact, includes, negate } = require( 'lodash' );
*/
const PACKAGES_DIR = path.resolve( __dirname, '../../packages' );
-const {
- /**
- * Comma-separated string of packages to include in build.
- *
- * @type {string}
- */
- INCLUDE_PACKAGES,
-
- /**
- * Comma-separated string of packages to exclude from build.
- *
- * @type {string}
- */
- EXCLUDE_PACKAGES,
-} = process.env;
-
-/**
- * Given a comma-separated string, returns a filter function which returns true
- * if the item is contained within as a comma-separated entry.
- *
- * @param {Function} filterFn Filter function to call with item to test.
- * @param {string} list Comma-separated list of items.
- *
- * @return {Function} Filter function.
- */
-const createCommaSeparatedFilter = ( filterFn, list ) => {
- const listItems = list.split( ',' );
- return ( item ) => filterFn( listItems, item );
-};
-
/**
* Returns true if the given base file name for a file within the packages
* directory is itself a directory.
@@ -62,11 +32,7 @@ function isDirectory( file ) {
*
* @return {boolean} Whether to include file in build.
*/
-const filterPackages = overEvery( compact( [
- isDirectory,
- INCLUDE_PACKAGES && createCommaSeparatedFilter( includes, INCLUDE_PACKAGES ),
- EXCLUDE_PACKAGES && createCommaSeparatedFilter( negate( includes ), EXCLUDE_PACKAGES ),
-] ) );
+const filterPackages = overEvery( isDirectory );
/**
* Returns the absolute path of all WordPress packages
diff --git a/package-lock.json b/package-lock.json
index 4c9b3ccb412e8..064963872225f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2496,10 +2496,7 @@
},
"@wordpress/babel-plugin-import-jsx-pragma": {
"version": "file:packages/babel-plugin-import-jsx-pragma",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.3.1"
- }
+ "dev": true
},
"@wordpress/babel-plugin-makepot": {
"version": "file:packages/babel-plugin-makepot",
@@ -2521,6 +2518,7 @@
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@babel/runtime": "^7.3.1",
+ "@wordpress/babel-plugin-import-jsx-pragma": "file:packages/babel-plugin-import-jsx-pragma",
"@wordpress/browserslist-config": "file:packages/browserslist-config"
}
},
@@ -2975,7 +2973,6 @@
"version": "file:packages/postcss-themes",
"dev": true,
"requires": {
- "@babel/runtime": "^7.3.1",
"autoprefixer": "^9.4.5",
"postcss": "^7.0.13",
"postcss-color-function": "^4.0.1"
diff --git a/package.json b/package.json
index 63f478e6c6fa3..eb1b308d87f2a 100644
--- a/package.json
+++ b/package.json
@@ -150,8 +150,8 @@
"scripts": {
"prebuild": "npm run check-engines",
"clean:packages": "rimraf ./packages/*/build ./packages/*/build-module ./packages/*/build-style",
- "prebuild:packages": "npm run clean:packages && lerna run build && cross-env INCLUDE_PACKAGES=babel-plugin-import-jsx-pragma,postcss-themes,jest-console SKIP_JSX_PRAGMA_TRANSFORM=1 node ./bin/packages/build.js",
- "build:packages": "cross-env EXCLUDE_PACKAGES=babel-plugin-import-jsx-pragma,jest-console,postcss-themes node ./bin/packages/build.js",
+ "prebuild:packages": "npm run clean:packages && lerna run build",
+ "build:packages": "node ./bin/packages/build.js",
"build": "npm run build:packages && wp-scripts build",
"check-engines": "wp-scripts check-engines",
"check-licenses": "concurrently \"wp-scripts check-licenses --prod --gpl2\" \"wp-scripts check-licenses --dev\"",
diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
index ffa68f4404f0d..32ad201bf1884 100644
--- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
+++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
@@ -3,6 +3,7 @@
### Breaking Change
- Plugin skips now adding import JSX pragma when the scope variable is defined for all JSX elements ([#13809](https://github.com/WordPress/gutenberg/pull/13809)).
+- Stop using Babel transpilation internally and set node 8 as a minimal version required ([#13540](https://github.com/WordPress/gutenberg/pull/13540)).
## 1.1.0 (2018-09-05)
diff --git a/packages/babel-plugin-import-jsx-pragma/src/index.js b/packages/babel-plugin-import-jsx-pragma/index.js
similarity index 96%
rename from packages/babel-plugin-import-jsx-pragma/src/index.js
rename to packages/babel-plugin-import-jsx-pragma/index.js
index 68e94e1ffc37d..d431b6823aea3 100644
--- a/packages/babel-plugin-import-jsx-pragma/src/index.js
+++ b/packages/babel-plugin-import-jsx-pragma/index.js
@@ -26,15 +26,12 @@ const DEFAULT_OPTIONS = {
*
* @return {Object} Babel transform plugin.
*/
-export default function( babel ) {
+module.exports = function( babel ) {
const { types: t } = babel;
function getOptions( state ) {
if ( ! state._options ) {
- state._options = {
- ...DEFAULT_OPTIONS,
- ...state.opts,
- };
+ state._options = Object.assign( {}, DEFAULT_OPTIONS, state.opts );
}
return state._options;
@@ -106,4 +103,4 @@ export default function( babel ) {
},
},
};
-}
+};
diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json
index 3f9efdc87ffa1..c06f76a26f3bb 100644
--- a/packages/babel-plugin-import-jsx-pragma/package.json
+++ b/packages/babel-plugin-import-jsx-pragma/package.json
@@ -20,15 +20,13 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
- "build",
- "build-module"
+ "index.js"
],
- "main": "build/index.js",
- "module": "build-module/index.js",
- "dependencies": {
- "@babel/runtime": "^7.3.1"
- },
+ "main": "index.js",
"peerDependencies": {
"@babel/core": "^7.0.0"
},
diff --git a/packages/babel-plugin-import-jsx-pragma/test/index.js b/packages/babel-plugin-import-jsx-pragma/test/index.js
index d70cf2313c540..a10207a5d5563 100644
--- a/packages/babel-plugin-import-jsx-pragma/test/index.js
+++ b/packages/babel-plugin-import-jsx-pragma/test/index.js
@@ -6,7 +6,7 @@ import { transformSync } from '@babel/core';
/**
* Internal dependencies
*/
-import plugin from '../src';
+import plugin from '../';
describe( 'babel-plugin-import-jsx-pragma', () => {
function getTransformedCode( source, options = {} ) {
diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json
index fae36a7f7ac3d..10b4d4eb90d02 100644
--- a/packages/babel-plugin-makepot/package.json
+++ b/packages/babel-plugin-makepot/package.json
@@ -19,6 +19,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
"build",
"build-module"
diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md
index 40e47005778e4..3fe7a9f29b100 100644
--- a/packages/babel-preset-default/CHANGELOG.md
+++ b/packages/babel-preset-default/CHANGELOG.md
@@ -3,6 +3,7 @@
## Breaking Change
- Removed `babel-core` dependency acting as Babel 7 bridge ([#13922](https://github.com/WordPress/gutenberg/pull/13922). Ensure all references to `babel-core` are replaced with `@babel/core` .
+- Preset updated to include `@wordpress/babel-plugin-import-jsx-pragma` plugin integration ([#13540](https://github.com/WordPress/gutenberg/pull/13540)).
## 3.0.0 (2018-09-30)
diff --git a/packages/babel-preset-default/index.js b/packages/babel-preset-default/index.js
index a2bfedebe1b37..44a0392cd82cb 100644
--- a/packages/babel-preset-default/index.js
+++ b/packages/babel-preset-default/index.js
@@ -15,6 +15,14 @@ module.exports = function( api ) {
].filter( Boolean ),
plugins: [
'@babel/plugin-proposal-object-rest-spread',
+ [
+ '@wordpress/babel-plugin-import-jsx-pragma',
+ {
+ scopeVariable: 'createElement',
+ source: '@wordpress/element',
+ isDefault: false,
+ },
+ ],
[ '@babel/plugin-transform-react-jsx', {
pragma: 'createElement',
} ],
diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json
index a1f2f02e0ac88..1bf66c0c54e6c 100644
--- a/packages/babel-preset-default/package.json
+++ b/packages/babel-preset-default/package.json
@@ -22,6 +22,9 @@
"engines": {
"node": ">=8"
},
+ "files": [
+ "index.js"
+ ],
"main": "index.js",
"dependencies": {
"@babel/core": "^7.2.2",
@@ -31,6 +34,7 @@
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@babel/runtime": "^7.3.1",
+ "@wordpress/babel-plugin-import-jsx-pragma": "file:../babel-plugin-import-jsx-pragma",
"@wordpress/browserslist-config": "file:../browserslist-config"
},
"peerDependencies": {
diff --git a/packages/custom-templated-path-webpack-plugin/package.json b/packages/custom-templated-path-webpack-plugin/package.json
index 49ca17003c257..02cd1d4c4d7c9 100644
--- a/packages/custom-templated-path-webpack-plugin/package.json
+++ b/packages/custom-templated-path-webpack-plugin/package.json
@@ -18,6 +18,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
"build",
"build-module"
diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json
index 071541ed4449c..f31d67677475e 100644
--- a/packages/e2e-test-utils/package.json
+++ b/packages/e2e-test-utils/package.json
@@ -18,6 +18,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
"build",
"build-module"
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index 881a497511505..c7e096b781d76 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -18,6 +18,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"dependencies": {
"@wordpress/e2e-test-utils": "file:../e2e-test-utils",
"@wordpress/jest-console": "file:../jest-console",
diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json
index d39dbe4b23026..2b54b68bb8ff8 100644
--- a/packages/jest-console/package.json
+++ b/packages/jest-console/package.json
@@ -19,6 +19,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
"build",
"build-module"
diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json
index 42110a066fe89..bc73370bb4706 100644
--- a/packages/jest-preset-default/package.json
+++ b/packages/jest-preset-default/package.json
@@ -21,6 +21,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
"scripts",
"jest-preset.json"
diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json
index 6c5e7ba6bc88d..c8a534ec613d4 100644
--- a/packages/jest-puppeteer-axe/package.json
+++ b/packages/jest-puppeteer-axe/package.json
@@ -20,6 +20,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
"build",
"build-module"
diff --git a/packages/library-export-default-webpack-plugin/package.json b/packages/library-export-default-webpack-plugin/package.json
index e2a2881f72902..c64a2f3bee854 100644
--- a/packages/library-export-default-webpack-plugin/package.json
+++ b/packages/library-export-default-webpack-plugin/package.json
@@ -18,6 +18,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
"build",
"build-module"
diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json
index f2dabfdf29af2..f61270594fd16 100644
--- a/packages/npm-package-json-lint-config/package.json
+++ b/packages/npm-package-json-lint-config/package.json
@@ -18,6 +18,9 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"main": "index.js",
"peerDependencies": {
"npm-package-json-lint": ">=3.3.1"
diff --git a/packages/postcss-themes/CHANGELOG.md b/packages/postcss-themes/CHANGELOG.md
new file mode 100644
index 0000000000000..23a814422fd86
--- /dev/null
+++ b/packages/postcss-themes/CHANGELOG.md
@@ -0,0 +1,5 @@
+## 2.0.0 (Unreleased)
+
+### Breaking change
+
+- Stop using Babel transpilation internally and set node 8 as a minimal version required ([#13540](https://github.com/WordPress/gutenberg/pull/13540)).
diff --git a/packages/postcss-themes/src/index.js b/packages/postcss-themes/index.js
similarity index 98%
rename from packages/postcss-themes/src/index.js
rename to packages/postcss-themes/index.js
index 5018a0dd39a4e..9c7dd80c64501 100644
--- a/packages/postcss-themes/src/index.js
+++ b/packages/postcss-themes/index.js
@@ -1,3 +1,6 @@
+/**
+ * External dependencies
+ */
const postcss = require( 'postcss' );
module.exports = postcss.plugin( 'postcss-themes', function( options ) {
diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json
index cafbbdf93e6b4..8a089ee2c8ccf 100644
--- a/packages/postcss-themes/package.json
+++ b/packages/postcss-themes/package.json
@@ -21,13 +21,14 @@
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
+ "engines": {
+ "node": ">=8"
+ },
"files": [
- "build",
- "build-module"
+ "index.js"
],
- "main": "build/index.js",
+ "main": "index.js",
"dependencies": {
- "@babel/runtime": "^7.3.1",
"autoprefixer": "^9.4.5",
"postcss": "^7.0.13",
"postcss-color-function": "^4.0.1"
diff --git a/packages/postcss-themes/test/index.js b/packages/postcss-themes/test/index.js
index 9a670ba55a588..45a058d6e2a31 100644
--- a/packages/postcss-themes/test/index.js
+++ b/packages/postcss-themes/test/index.js
@@ -6,7 +6,7 @@ import postcss from 'postcss';
/**
* Internal dependencies
*/
-import plugin from '../src';
+import plugin from '../';
/**
* Module constants
From cb809b98654151416646c56f5de42c93ce656315 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Mon, 25 Feb 2019 12:22:03 +0100
Subject: [PATCH 022/169] Use the block editor store instead of the editor one
(#13105)
---
lib/packages-dependencies.php | 2 ++
package-lock.json | 2 ++
packages/block-library/package.json | 1 +
packages/block-library/src/block/edit.js | 9 ++++++---
packages/block-library/src/html/edit.js | 2 +-
packages/block-library/src/image/edit.js | 2 +-
packages/block-library/src/index.js | 2 ++
packages/block-library/src/missing/index.js | 2 +-
packages/block-library/src/paragraph/edit.js | 2 +-
packages/block-library/src/pullquote/index.js | 2 +-
packages/edit-post/package.json | 1 +
.../plugin-block-settings-menu-group.js | 2 +-
.../components/header/header-toolbar/index.js | 3 ++-
.../edit-post/src/components/header/index.js | 2 +-
.../components/header/mode-switcher/index.js | 2 +-
.../src/components/keyboard-shortcuts/index.js | 4 ++--
.../edit-post/src/components/layout/index.js | 2 +-
.../options-modal/meta-boxes-section.js | 3 ++-
.../options/enable-custom-fields.js | 2 +-
.../components/sidebar/settings-header/index.js | 2 +-
.../src/components/text-editor/index.js | 2 +-
.../src/hooks/validate-multiple-use/index.js | 4 ++--
packages/edit-post/src/index.js | 1 +
.../edit-post/src/prevent-event-discovery.js | 2 +-
packages/edit-post/src/store/effects.js | 4 ++--
.../src/components/alignment-toolbar/index.js | 2 +-
.../src/components/autocompleters/block.js | 6 +++---
.../src/components/autosave-monitor/index.js | 4 ++--
.../src/components/block-actions/index.js | 10 +++++-----
.../components/block-alignment-toolbar/index.js | 4 ++--
.../src/components/block-draggable/index.js | 2 +-
.../src/components/block-drop-zone/index.js | 4 ++--
.../src/components/block-inspector/index.js | 2 +-
.../src/components/block-list-appender/index.js | 2 +-
.../src/components/block-list/block-html.js | 4 ++--
.../block-list/block-invalid-warning.js | 4 ++--
.../editor/src/components/block-list/block.js | 17 ++++++++++-------
.../src/components/block-list/breadcrumb.js | 2 +-
.../src/components/block-list/hover-area.js | 2 +-
.../editor/src/components/block-list/index.js | 4 ++--
.../components/block-list/insertion-point.js | 2 +-
.../src/components/block-list/multi-controls.js | 2 +-
.../editor/src/components/block-mover/index.js | 4 ++--
.../src/components/block-navigation/dropdown.js | 2 +-
.../src/components/block-navigation/index.js | 4 ++--
.../components/block-selection-clearer/index.js | 4 ++--
.../block-html-convert-button.js | 4 ++--
.../block-settings-menu/block-mode-toggle.js | 4 ++--
.../block-unknown-convert-button.js | 4 ++--
.../src/components/block-settings-menu/index.js | 2 +-
.../reusable-block-convert-button.js | 2 ++
.../reusable-block-delete-button.js | 9 +++------
.../editor/src/components/block-styles/index.js | 4 ++--
.../src/components/block-switcher/index.js | 4 ++--
.../block-switcher/multi-blocks-switcher.js | 2 +-
.../editor/src/components/block-title/index.js | 2 +-
.../src/components/block-toolbar/index.js | 2 +-
.../color-palette/with-color-context.js | 2 +-
.../editor/src/components/colors/with-colors.js | 2 +-
.../editor/src/components/copy-handler/index.js | 4 ++--
.../components/default-block-appender/index.js | 4 ++--
.../default-block-appender/index.native.js | 4 ++--
.../src/components/document-outline/check.js | 2 +-
.../src/components/document-outline/index.js | 4 ++--
.../components/font-sizes/font-size-picker.js | 2 +-
.../components/font-sizes/with-font-sizes.js | 2 +-
.../global-keyboard-shortcuts/save-shortcut.js | 4 +---
.../visual-editor-shortcuts.js | 8 ++++++--
.../editor/src/components/inner-blocks/index.js | 4 ++--
.../components/inserter-with-shortcuts/index.js | 6 +++---
.../src/components/inserter/child-blocks.js | 2 +-
.../editor/src/components/inserter/index.js | 7 ++++++-
packages/editor/src/components/inserter/menu.js | 14 +++++++++-----
.../multi-select-scroll-into-view/index.js | 2 +-
.../multi-selection-inspector/index.js | 2 +-
.../src/components/observe-typing/index.js | 4 ++--
.../src/components/page-attributes/check.js | 5 ++++-
.../src/components/page-attributes/template.js | 3 ++-
.../editor/src/components/post-format/check.js | 3 ++-
.../src/components/post-locked-modal/index.js | 5 ++++-
.../editor/src/components/post-title/index.js | 7 +++++--
.../src/components/post-title/index.native.js | 7 +++++--
.../preserve-scroll-in-reorder/index.js | 4 ++--
.../editor/src/components/rich-text/index.js | 4 +++-
.../components/skip-to-selected-block/index.js | 2 +-
.../src/components/table-of-contents/index.js | 2 +-
.../src/components/table-of-contents/panel.js | 2 +-
.../template-validation-notice/index.js | 4 ++--
.../editor/src/components/writing-flow/index.js | 4 ++--
packages/editor/src/hooks/align.js | 2 +-
packages/editor/src/utils/media-upload/index.js | 8 ++++----
91 files changed, 186 insertions(+), 143 deletions(-)
diff --git a/lib/packages-dependencies.php b/lib/packages-dependencies.php
index e2f135bda24d9..47c7d15422940 100644
--- a/lib/packages-dependencies.php
+++ b/lib/packages-dependencies.php
@@ -27,6 +27,7 @@
'wp-api-fetch',
'wp-autop',
'wp-blob',
+ 'wp-block-editor',
'wp-blocks',
'wp-components',
'wp-compose',
@@ -121,6 +122,7 @@
'media-views',
'wp-a11y',
'wp-api-fetch',
+ 'wp-block-editor',
'wp-block-library',
'wp-blocks',
'wp-components',
diff --git a/package-lock.json b/package-lock.json
index 064963872225f..a86ed000f301b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2549,6 +2549,7 @@
"@babel/runtime": "^7.3.1",
"@wordpress/autop": "file:packages/autop",
"@wordpress/blob": "file:packages/blob",
+ "@wordpress/block-editor": "file:packages/block-editor",
"@wordpress/blocks": "file:packages/blocks",
"@wordpress/components": "file:packages/components",
"@wordpress/compose": "file:packages/compose",
@@ -2739,6 +2740,7 @@
"@babel/runtime": "^7.3.1",
"@wordpress/a11y": "file:packages/a11y",
"@wordpress/api-fetch": "file:packages/api-fetch",
+ "@wordpress/block-editor": "file:packages/block-editor",
"@wordpress/block-library": "file:packages/block-library",
"@wordpress/blocks": "file:packages/blocks",
"@wordpress/components": "file:packages/components",
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index 9b5d871edf42f..5847272862360 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -24,6 +24,7 @@
"@babel/runtime": "^7.3.1",
"@wordpress/autop": "file:../autop",
"@wordpress/blob": "file:../blob",
+ "@wordpress/block-editor": "file:../block-editor",
"@wordpress/blocks": "file:../blocks",
"@wordpress/components": "file:../components",
"@wordpress/compose": "file:../compose",
diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js
index 62e4acf0640d1..5db7bda7c7f60 100644
--- a/packages/block-library/src/block/edit.js
+++ b/packages/block-library/src/block/edit.js
@@ -150,10 +150,11 @@ export default compose( [
__experimentalGetReusableBlock: getReusableBlock,
__experimentalIsFetchingReusableBlock: isFetchingReusableBlock,
__experimentalIsSavingReusableBlock: isSavingReusableBlock,
- getBlock,
} = select( 'core/editor' );
const { canUser } = select( 'core' );
-
+ const {
+ getBlock,
+ } = select( 'core/block-editor' );
const { ref } = ownProps.attributes;
const reusableBlock = getReusableBlock( ref );
@@ -168,10 +169,12 @@ export default compose( [
withDispatch( ( dispatch, ownProps ) => {
const {
__experimentalFetchReusableBlocks: fetchReusableBlocks,
- updateBlockAttributes,
__experimentalUpdateReusableBlockTitle: updateReusableBlockTitle,
__experimentalSaveReusableBlock: saveReusableBlock,
} = dispatch( 'core/editor' );
+ const {
+ updateBlockAttributes,
+ } = dispatch( 'core/block-editor' );
const { ref } = ownProps.attributes;
return {
diff --git a/packages/block-library/src/html/edit.js b/packages/block-library/src/html/edit.js
index 44aa79544415b..6dd2bb6f1fd92 100644
--- a/packages/block-library/src/html/edit.js
+++ b/packages/block-library/src/html/edit.js
@@ -87,7 +87,7 @@ class HTMLEdit extends Component {
}
}
export default withSelect( ( select ) => {
- const { getEditorSettings } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
return {
styles: getEditorSettings().styles,
};
diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js
index 44534956f5b5a..deaeaf79766dc 100644
--- a/packages/block-library/src/image/edit.js
+++ b/packages/block-library/src/image/edit.js
@@ -705,7 +705,7 @@ class ImageEdit extends Component {
export default compose( [
withSelect( ( select, props ) => {
const { getMedia } = select( 'core' );
- const { getEditorSettings } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
const { id } = props.attributes;
const { maxWidth, isRTL, imageSizes } = getEditorSettings();
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index 4b15aac33c4a4..eb6cd561d887a 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -2,6 +2,8 @@
* WordPress dependencies
*/
import '@wordpress/core-data';
+import '@wordpress/block-editor';
+import '@wordpress/editor';
import {
registerBlockType,
setDefaultBlockName,
diff --git a/packages/block-library/src/missing/index.js b/packages/block-library/src/missing/index.js
index 8ec90b2d5cd44..62ad7ed96b999 100644
--- a/packages/block-library/src/missing/index.js
+++ b/packages/block-library/src/missing/index.js
@@ -43,7 +43,7 @@ function MissingBlockWarning( { attributes, convertToHTML } ) {
}
const edit = withDispatch( ( dispatch, { clientId, attributes } ) => {
- const { replaceBlock } = dispatch( 'core/editor' );
+ const { replaceBlock } = dispatch( 'core/block-editor' );
return {
convertToHTML() {
replaceBlock( clientId, createBlock( 'core/html', {
diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js
index 4d57376ee10c3..2aaad4c8060ff 100644
--- a/packages/block-library/src/paragraph/edit.js
+++ b/packages/block-library/src/paragraph/edit.js
@@ -259,7 +259,7 @@ const ParagraphEdit = compose( [
withFontSizes( 'fontSize' ),
applyFallbackStyles,
withSelect( ( select ) => {
- const { getEditorSettings } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
return {
isRTL: getEditorSettings().isRTL,
diff --git a/packages/block-library/src/pullquote/index.js b/packages/block-library/src/pullquote/index.js
index b7782b68a0551..beaf1d9549aaa 100644
--- a/packages/block-library/src/pullquote/index.js
+++ b/packages/block-library/src/pullquote/index.js
@@ -100,7 +100,7 @@ export const settings = {
// Is normal style and a named color is being used, we need to retrieve the color value to set the style,
// as there is no expectation that themes create classes that set border colors.
} else if ( mainColor ) {
- const colors = get( select( 'core/editor' ).getEditorSettings(), [ 'colors' ], [] );
+ const colors = get( select( 'core/block-editor' ).getEditorSettings(), [ 'colors' ], [] );
const colorObject = getColorObjectByAttributeValues( colors, mainColor );
figureStyles = {
borderColor: colorObject.color,
diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json
index 0327ee5121f38..74756043aec27 100644
--- a/packages/edit-post/package.json
+++ b/packages/edit-post/package.json
@@ -23,6 +23,7 @@
"@babel/runtime": "^7.3.1",
"@wordpress/a11y": "file:../a11y",
"@wordpress/api-fetch": "file:../api-fetch",
+ "@wordpress/block-editor": "file:../block-editor",
"@wordpress/block-library": "file:../block-library",
"@wordpress/blocks": "file:../blocks",
"@wordpress/components": "file:../components",
diff --git a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-group.js b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-group.js
index 24859cb92fcc3..dfeb824f600fa 100644
--- a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-group.js
+++ b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-group.js
@@ -27,7 +27,7 @@ const PluginBlockSettingsMenuGroupSlot = ( { fillProps, selectedBlocks } ) => {
};
PluginBlockSettingsMenuGroup.Slot = withSelect( ( select, { fillProps: { clientIds } } ) => ( {
- selectedBlocks: select( 'core/editor' ).getBlocksByClientId( clientIds ),
+ selectedBlocks: select( 'core/block-editor' ).getBlocksByClientId( clientIds ),
} ) )( PluginBlockSettingsMenuGroupSlot );
export default PluginBlockSettingsMenuGroup;
diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js
index ef56ca64b3ad7..87a24a85e2a8a 100644
--- a/packages/edit-post/src/components/header/header-toolbar/index.js
+++ b/packages/edit-post/src/components/header/header-toolbar/index.js
@@ -56,7 +56,8 @@ function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter } ) {
export default compose( [
withSelect( ( select ) => ( {
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
- showInserter: select( 'core/edit-post' ).getEditorMode() === 'visual' && select( 'core/editor' ).getEditorSettings().richEditingEnabled,
+ // This setting (richEditingEnabled) should not live in the block editor's setting.
+ showInserter: select( 'core/edit-post' ).getEditorMode() === 'visual' && select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
} ) ),
withViewportMatch( { isLargeViewport: 'medium' } ),
] )( HeaderToolbar );
diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js
index 7b59784d5d83e..2a16364c7bd2a 100644
--- a/packages/edit-post/src/components/header/index.js
+++ b/packages/edit-post/src/components/header/index.js
@@ -87,7 +87,7 @@ export default compose(
isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(),
} ) ),
withDispatch( ( dispatch, ownProps, { select } ) => {
- const { getBlockSelectionStart } = select( 'core/editor' );
+ const { getBlockSelectionStart } = select( 'core/block-editor' );
const { openGeneralSidebar, closeGeneralSidebar } = dispatch( 'core/edit-post' );
return {
diff --git a/packages/edit-post/src/components/header/mode-switcher/index.js b/packages/edit-post/src/components/header/mode-switcher/index.js
index 52ca0b78cfc94..058d9c74eeb8c 100644
--- a/packages/edit-post/src/components/header/mode-switcher/index.js
+++ b/packages/edit-post/src/components/header/mode-switcher/index.js
@@ -49,7 +49,7 @@ function ModeSwitcher( { onSwitch, mode } ) {
export default compose( [
withSelect( ( select ) => ( {
- isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
mode: select( 'core/edit-post' ).getEditorMode(),
} ) ),
ifCondition( ( { isRichEditingEnabled } ) => isRichEditingEnabled ),
diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js
index 3fd18ca87a09d..b5c09178efd68 100644
--- a/packages/edit-post/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js
@@ -55,7 +55,7 @@ class EditorModeKeyboardShortcuts extends Component {
export default compose( [
withSelect( ( select ) => ( {
- isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
mode: select( 'core/edit-post' ).getEditorMode(),
isEditorSidebarOpen: select( 'core/edit-post' ).isEditorSidebarOpened(),
} ) ),
@@ -64,7 +64,7 @@ export default compose( [
dispatch( 'core/edit-post' ).switchEditorMode( mode );
},
openSidebar() {
- const { getBlockSelectionStart } = select( 'core/editor' );
+ const { getBlockSelectionStart } = select( 'core/block-editor' );
const sidebarToOpen = getBlockSelectionStart() ? 'edit-post/block' : 'edit-post/document';
dispatch( 'core/edit-post' ).openGeneralSidebar( sidebarToOpen );
},
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js
index f0876f4672287..76c10e8a78032 100644
--- a/packages/edit-post/src/components/layout/index.js
+++ b/packages/edit-post/src/components/layout/index.js
@@ -137,7 +137,7 @@ export default compose(
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(),
isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(),
- isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
} ) ),
withDispatch( ( dispatch ) => {
const { closePublishSidebar, togglePublishSidebar } = dispatch( 'core/edit-post' );
diff --git a/packages/edit-post/src/components/options-modal/meta-boxes-section.js b/packages/edit-post/src/components/options-modal/meta-boxes-section.js
index bbec30e0eadb7..a5b8e0e85ad50 100644
--- a/packages/edit-post/src/components/options-modal/meta-boxes-section.js
+++ b/packages/edit-post/src/components/options-modal/meta-boxes-section.js
@@ -34,10 +34,11 @@ export function MetaBoxesSection( { areCustomFieldsRegistered, metaBoxes, ...sec
}
export default withSelect( ( select ) => {
- const { getEditorSettings } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
const { getAllMetaBoxes } = select( 'core/edit-post' );
return {
+ // This setting should not live in the block editor's store.
areCustomFieldsRegistered: getEditorSettings().enableCustomFields !== undefined,
metaBoxes: getAllMetaBoxes(),
};
diff --git a/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js b/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js
index 140c6f1c46d2d..e9a319efbebfe 100644
--- a/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js
+++ b/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js
@@ -43,5 +43,5 @@ export class EnableCustomFieldsOption extends Component {
}
export default withSelect( ( select ) => ( {
- isChecked: !! select( 'core/editor' ).getEditorSettings().enableCustomFields,
+ isChecked: !! select( 'core/block-editor' ).getEditorSettings().enableCustomFields,
} ) )( EnableCustomFieldsOption );
diff --git a/packages/edit-post/src/components/sidebar/settings-header/index.js b/packages/edit-post/src/components/sidebar/settings-header/index.js
index e2866c231a1c9..eeb95a872166f 100644
--- a/packages/edit-post/src/components/sidebar/settings-header/index.js
+++ b/packages/edit-post/src/components/sidebar/settings-header/index.js
@@ -57,7 +57,7 @@ const SettingsHeader = ( { openDocumentSettings, openBlockSettings, sidebarName
export default withDispatch( ( dispatch ) => {
const { openGeneralSidebar } = dispatch( 'core/edit-post' );
- const { clearSelectedBlock } = dispatch( 'core/editor' );
+ const { clearSelectedBlock } = dispatch( 'core/block-editor' );
return {
openDocumentSettings() {
openGeneralSidebar( 'edit-post/document' );
diff --git a/packages/edit-post/src/components/text-editor/index.js b/packages/edit-post/src/components/text-editor/index.js
index 882e97dd7e83c..0cebd6e7396ad 100644
--- a/packages/edit-post/src/components/text-editor/index.js
+++ b/packages/edit-post/src/components/text-editor/index.js
@@ -38,7 +38,7 @@ function TextEditor( { onExit, isRichEditingEnabled } ) {
export default compose(
withSelect( ( select ) => ( {
- isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
} ) ),
withDispatch( ( dispatch ) => {
return {
diff --git a/packages/edit-post/src/hooks/validate-multiple-use/index.js b/packages/edit-post/src/hooks/validate-multiple-use/index.js
index 74d6b183ee62e..8a9f15a2c1f49 100644
--- a/packages/edit-post/src/hooks/validate-multiple-use/index.js
+++ b/packages/edit-post/src/hooks/validate-multiple-use/index.js
@@ -43,7 +43,7 @@ const enhance = compose(
// Otherwise, only pass `originalBlockClientId` if it refers to a different
// block from the current one.
- const blocks = select( 'core/editor' ).getBlocks();
+ const blocks = select( 'core/block-editor' ).getBlocks();
const firstOfSameType = find( blocks, ( { name } ) => block.name === name );
const isInvalid = firstOfSameType && firstOfSameType.clientId !== block.clientId;
return {
@@ -51,7 +51,7 @@ const enhance = compose(
};
} ),
withDispatch( ( dispatch, { originalBlockClientId } ) => ( {
- selectFirst: () => dispatch( 'core/editor' ).selectBlock( originalBlockClientId ),
+ selectFirst: () => dispatch( 'core/block-editor' ).selectBlock( originalBlockClientId ),
} ) ),
);
diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js
index 59e870e7929da..ecfac09f9347e 100644
--- a/packages/edit-post/src/index.js
+++ b/packages/edit-post/src/index.js
@@ -2,6 +2,7 @@
* WordPress dependencies
*/
import '@wordpress/core-data';
+import '@wordpress/block-editor';
import '@wordpress/editor';
import '@wordpress/nux';
import '@wordpress/viewport';
diff --git a/packages/edit-post/src/prevent-event-discovery.js b/packages/edit-post/src/prevent-event-discovery.js
index 97e7b7d8a8fbb..36e58f798752c 100644
--- a/packages/edit-post/src/prevent-event-discovery.js
+++ b/packages/edit-post/src/prevent-event-discovery.js
@@ -8,7 +8,7 @@ export default {
}
event.preventDefault();
- window.wp.data.dispatch( 'core/editor' ).insertBlock(
+ window.wp.data.dispatch( 'core/block-editor' ).insertBlock(
window.wp.blocks.createBlock( 'core/paragraph', {
content: '🐡🐢🦀🐤🦋🐘🐧🐹🦁🦄🦍🐼🐿🎃🐴🐝🐆🦕🦔🌱🍇π🍌🐉💧🥨🌌🍂🍠🥦🥚🥝🎟🥥🥒🛵🥖🍒🍯🎾🎲🐺🐚🐮⌛️',
} )
diff --git a/packages/edit-post/src/store/effects.js b/packages/edit-post/src/store/effects.js
index a177c36c1b761..673025c3fae51 100644
--- a/packages/edit-post/src/store/effects.js
+++ b/packages/edit-post/src/store/effects.js
@@ -119,7 +119,7 @@ const effects = {
SWITCH_MODE( action ) {
// Unselect blocks when we switch to the code editor.
if ( action.mode !== 'visual' ) {
- dispatch( 'core/editor' ).clearSelectedBlock();
+ dispatch( 'core/block-editor' ).clearSelectedBlock();
}
const message = action.mode === 'visual' ? __( 'Visual editor selected' ) : __( 'Code editor selected' );
@@ -128,7 +128,7 @@ const effects = {
INIT( _, store ) {
// Select the block settings tab when the selected block changes
subscribe( onChangeListener(
- () => !! select( 'core/editor' ).getBlockSelectionStart(),
+ () => !! select( 'core/block-editor' ).getBlockSelectionStart(),
( hasBlockSelection ) => {
if ( ! select( 'core/edit-post' ).isEditorSidebarOpened() ) {
return;
diff --git a/packages/editor/src/components/alignment-toolbar/index.js b/packages/editor/src/components/alignment-toolbar/index.js
index e06ee34f4d7d6..bf5fe27650e2a 100644
--- a/packages/editor/src/components/alignment-toolbar/index.js
+++ b/packages/editor/src/components/alignment-toolbar/index.js
@@ -69,7 +69,7 @@ export default compose(
} ),
withViewportMatch( { isLargeViewport: 'medium' } ),
withSelect( ( select, { clientId, isLargeViewport, isCollapsed } ) => {
- const { getBlockRootClientId, getEditorSettings } = select( 'core/editor' );
+ const { getBlockRootClientId, getEditorSettings } = select( 'core/block-editor' );
return {
isCollapsed: isCollapsed || ! isLargeViewport || (
! getEditorSettings().hasFixedToolbar &&
diff --git a/packages/editor/src/components/autocompleters/block.js b/packages/editor/src/components/autocompleters/block.js
index d40b18d55c38b..2945ea4322786 100644
--- a/packages/editor/src/components/autocompleters/block.js
+++ b/packages/editor/src/components/autocompleters/block.js
@@ -17,7 +17,7 @@ import BlockIcon from '../block-icon';
* be placed.
*/
function defaultGetBlockInsertionParentClientId() {
- return select( 'core/editor' ).getBlockInsertionPoint().rootClientId;
+ return select( 'core/block-editor' ).getBlockInsertionPoint().rootClientId;
}
/**
@@ -30,7 +30,7 @@ function defaultGetBlockInsertionParentClientId() {
* parent.
*/
function defaultGetInserterItems( rootClientId ) {
- return select( 'core/editor' ).getInserterItems( rootClientId );
+ return select( 'core/block-editor' ).getInserterItems( rootClientId );
}
/**
@@ -40,7 +40,7 @@ function defaultGetInserterItems( rootClientId ) {
* block is selected.
*/
function defaultGetSelectedBlockName() {
- const { getSelectedBlockClientId, getBlockName } = select( 'core/editor' );
+ const { getSelectedBlockClientId, getBlockName } = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
return selectedBlockClientId ? getBlockName( selectedBlockClientId ) : null;
}
diff --git a/packages/editor/src/components/autosave-monitor/index.js b/packages/editor/src/components/autosave-monitor/index.js
index c1be1b20e0a40..23cff18b19411 100644
--- a/packages/editor/src/components/autosave-monitor/index.js
+++ b/packages/editor/src/components/autosave-monitor/index.js
@@ -62,12 +62,12 @@ export default compose( [
const {
isEditedPostDirty,
isEditedPostAutosaveable,
- getEditorSettings,
getReferenceByDistinctEdits,
isAutosavingPost,
} = select( 'core/editor' );
- const { autosaveInterval } = getEditorSettings();
+ // This settings should not live in the block editor.
+ const { autosaveInterval } = select( 'core/block-editor' ).getEditorSettings();
return {
isDirty: isEditedPostDirty(),
diff --git a/packages/editor/src/components/block-actions/index.js b/packages/editor/src/components/block-actions/index.js
index b90325b0affd5..3b3f8032432e8 100644
--- a/packages/editor/src/components/block-actions/index.js
+++ b/packages/editor/src/components/block-actions/index.js
@@ -35,7 +35,7 @@ export default compose( [
getBlocksByClientId,
getTemplateLock,
getBlockRootClientId,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const blocks = getBlocksByClientId( props.clientIds );
const canDuplicate = every( blocks, ( block ) => {
@@ -65,7 +65,7 @@ export default compose( [
multiSelect,
removeBlocks,
insertDefaultBlock,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
return {
onDuplicate() {
@@ -73,7 +73,7 @@ export default compose( [
return;
}
- const { getBlockIndex } = select( 'core/editor' );
+ const { getBlockIndex } = select( 'core/block-editor' );
const lastSelectedIndex = getBlockIndex( last( castArray( clientIds ) ), rootClientId );
const clonedBlocks = blocks.map( ( block ) => cloneBlock( block ) );
insertBlocks(
@@ -95,14 +95,14 @@ export default compose( [
},
onInsertBefore() {
if ( ! isLocked ) {
- const { getBlockIndex } = select( 'core/editor' );
+ const { getBlockIndex } = select( 'core/block-editor' );
const firstSelectedIndex = getBlockIndex( first( castArray( clientIds ) ), rootClientId );
insertDefaultBlock( {}, rootClientId, firstSelectedIndex );
}
},
onInsertAfter() {
if ( ! isLocked ) {
- const { getBlockIndex } = select( 'core/editor' );
+ const { getBlockIndex } = select( 'core/block-editor' );
const lastSelectedIndex = getBlockIndex( last( castArray( clientIds ) ), rootClientId );
insertDefaultBlock( {}, rootClientId, lastSelectedIndex + 1 );
}
diff --git a/packages/editor/src/components/block-alignment-toolbar/index.js b/packages/editor/src/components/block-alignment-toolbar/index.js
index acfe1cb4ed344..0dcc4bda1412f 100644
--- a/packages/editor/src/components/block-alignment-toolbar/index.js
+++ b/packages/editor/src/components/block-alignment-toolbar/index.js
@@ -75,9 +75,9 @@ export default compose(
} ),
withViewportMatch( { isLargeViewport: 'medium' } ),
withSelect( ( select, { clientId, isLargeViewport, isCollapsed } ) => {
- const { getBlockRootClientId, getEditorSettings } = select( 'core/editor' );
+ const { getBlockRootClientId, getEditorSettings } = select( 'core/block-editor' );
return {
- wideControlsEnabled: select( 'core/editor' ).getEditorSettings().alignWide,
+ wideControlsEnabled: select( 'core/block-editor' ).getEditorSettings().alignWide,
isCollapsed: isCollapsed || ! isLargeViewport || (
! getEditorSettings().hasFixedToolbar &&
getBlockRootClientId( clientId )
diff --git a/packages/editor/src/components/block-draggable/index.js b/packages/editor/src/components/block-draggable/index.js
index 2f7c3436819a1..8818d58be9c2d 100644
--- a/packages/editor/src/components/block-draggable/index.js
+++ b/packages/editor/src/components/block-draggable/index.js
@@ -32,7 +32,7 @@ const BlockDraggable = ( { children, clientId, rootClientId, blockElementId, ind
};
export default withSelect( ( select, { clientId } ) => {
- const { getBlockIndex, getBlockRootClientId } = select( 'core/editor' );
+ const { getBlockIndex, getBlockRootClientId } = select( 'core/block-editor' );
const rootClientId = getBlockRootClientId( clientId );
return {
index: getBlockIndex( clientId, rootClientId ),
diff --git a/packages/editor/src/components/block-drop-zone/index.js b/packages/editor/src/components/block-drop-zone/index.js
index f1c95a53242a6..4434a6c338a97 100644
--- a/packages/editor/src/components/block-drop-zone/index.js
+++ b/packages/editor/src/components/block-drop-zone/index.js
@@ -138,7 +138,7 @@ export default compose(
insertBlocks,
updateBlockAttributes,
moveBlockToPosition,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
return {
insertBlocks( blocks, index ) {
@@ -156,7 +156,7 @@ export default compose(
};
} ),
withSelect( ( select, { rootClientId } ) => {
- const { getClientIdsOfDescendants, getTemplateLock, getBlockIndex } = select( 'core/editor' );
+ const { getClientIdsOfDescendants, getTemplateLock, getBlockIndex } = select( 'core/block-editor' );
return {
isLocked: !! getTemplateLock( rootClientId ),
getClientIdsOfDescendants,
diff --git a/packages/editor/src/components/block-inspector/index.js b/packages/editor/src/components/block-inspector/index.js
index ee2405a3bac14..f7cca99eb5af4 100644
--- a/packages/editor/src/components/block-inspector/index.js
+++ b/packages/editor/src/components/block-inspector/index.js
@@ -79,7 +79,7 @@ const BlockInspector = ( { selectedBlockClientId, selectedBlockName, blockType,
export default withSelect(
( select ) => {
- const { getSelectedBlockClientId, getSelectedBlockCount, getBlockName } = select( 'core/editor' );
+ const { getSelectedBlockClientId, getSelectedBlockCount, getBlockName } = select( 'core/block-editor' );
const { getBlockStyles } = select( 'core/blocks' );
const selectedBlockClientId = getSelectedBlockClientId();
const selectedBlockName = selectedBlockClientId && getBlockName( selectedBlockClientId );
diff --git a/packages/editor/src/components/block-list-appender/index.js b/packages/editor/src/components/block-list-appender/index.js
index 21ac587e42cd6..5af9c48a66f04 100644
--- a/packages/editor/src/components/block-list-appender/index.js
+++ b/packages/editor/src/components/block-list-appender/index.js
@@ -65,7 +65,7 @@ export default withSelect( ( select, { rootClientId } ) => {
getBlockOrder,
canInsertBlockType,
getTemplateLock,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
return {
isLocked: !! getTemplateLock( rootClientId ),
diff --git a/packages/editor/src/components/block-list/block-html.js b/packages/editor/src/components/block-list/block-html.js
index fc7b3da86ef08..9ec13410b367a 100644
--- a/packages/editor/src/components/block-list/block-html.js
+++ b/packages/editor/src/components/block-list/block-html.js
@@ -67,11 +67,11 @@ export class BlockHTML extends Component {
export default compose( [
withSelect( ( select, ownProps ) => ( {
- block: select( 'core/editor' ).getBlock( ownProps.clientId ),
+ block: select( 'core/block-editor' ).getBlock( ownProps.clientId ),
} ) ),
withDispatch( ( dispatch ) => ( {
onChange( clientId, attributes, originalContent, isValid ) {
- dispatch( 'core/editor' ).updateBlock( clientId, { attributes, originalContent, isValid } );
+ dispatch( 'core/block-editor' ).updateBlock( clientId, { attributes, originalContent, isValid } );
},
} ) ),
] )( BlockHTML );
diff --git a/packages/editor/src/components/block-list/block-invalid-warning.js b/packages/editor/src/components/block-list/block-invalid-warning.js
index 265e36c78a2e8..7d9efe1693523 100644
--- a/packages/editor/src/components/block-list/block-invalid-warning.js
+++ b/packages/editor/src/components/block-list/block-invalid-warning.js
@@ -101,10 +101,10 @@ const recoverBlock = ( { name, attributes, innerBlocks } ) => createBlock( name,
export default compose( [
withSelect( ( select, { clientId } ) => ( {
- block: select( 'core/editor' ).getBlock( clientId ),
+ block: select( 'core/block-editor' ).getBlock( clientId ),
} ) ),
withDispatch( ( dispatch, { block } ) => {
- const { replaceBlock } = dispatch( 'core/editor' );
+ const { replaceBlock } = dispatch( 'core/block-editor' );
return {
convertToClassic() {
diff --git a/packages/editor/src/components/block-list/block.js b/packages/editor/src/components/block-list/block.js
index 22b037c7e2e52..4a977903e44c7 100644
--- a/packages/editor/src/components/block-list/block.js
+++ b/packages/editor/src/components/block-list/block.js
@@ -637,7 +637,7 @@ const applyWithSelect = withSelect(
hasSelectedInnerBlock,
getTemplateLock,
__unstableGetBlockWithoutInnerBlocks,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const block = __unstableGetBlockWithoutInnerBlocks( clientId );
const isSelected = isBlockSelected( clientId );
const { hasFixedToolbar, focusMode } = getEditorSettings();
@@ -683,7 +683,6 @@ const applyWithSelect = withSelect(
);
const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
- const { getBlockSelectionStart } = select( 'core/editor' );
const {
updateBlockAttributes,
selectBlock,
@@ -694,7 +693,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
mergeBlocks,
replaceBlocks,
toggleSelection,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
return {
onChange( clientId, attributes ) {
@@ -711,7 +710,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
const { clientId, rootClientId } = ownProps;
const {
getBlockIndex,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const index = getBlockIndex( clientId, rootClientId );
insertDefaultBlock( {}, rootClientId, index + 1 );
},
@@ -719,7 +718,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
const { clientId, rootClientId } = ownProps;
const {
getBlockIndex,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const index = getBlockIndex( clientId, rootClientId );
insertBlocks( blocks, index + 1, rootClientId );
},
@@ -731,7 +730,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
const {
getPreviousBlockClientId,
getNextBlockClientId,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
if ( forward ) {
const nextBlockClientId = getNextBlockClientId( clientId );
@@ -749,7 +748,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
replaceBlocks( [ ownProps.clientId ], blocks );
},
onMetaChange( updatedMeta ) {
- const { getEditorSettings } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
const onChangeMeta = getEditorSettings().__experimentalMetaSource.onChange;
onChangeMeta( updatedMeta );
},
@@ -758,6 +757,10 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
return;
}
+ const {
+ getBlockSelectionStart,
+ } = select( 'core/block-editor' );
+
if ( getBlockSelectionStart() ) {
multiSelect( getBlockSelectionStart(), ownProps.clientId );
} else {
diff --git a/packages/editor/src/components/block-list/breadcrumb.js b/packages/editor/src/components/block-list/breadcrumb.js
index ca14b1d346c94..544ef992daf1f 100644
--- a/packages/editor/src/components/block-list/breadcrumb.js
+++ b/packages/editor/src/components/block-list/breadcrumb.js
@@ -68,7 +68,7 @@ export class BlockBreadcrumb extends Component {
export default compose( [
withSelect( ( select, ownProps ) => {
- const { getBlockRootClientId } = select( 'core/editor' );
+ const { getBlockRootClientId } = select( 'core/block-editor' );
const { clientId } = ownProps;
return {
diff --git a/packages/editor/src/components/block-list/hover-area.js b/packages/editor/src/components/block-list/hover-area.js
index ff0a16144b4f0..d7091552b4885 100644
--- a/packages/editor/src/components/block-list/hover-area.js
+++ b/packages/editor/src/components/block-list/hover-area.js
@@ -76,7 +76,7 @@ class HoverArea extends Component {
export default withSelect( ( select ) => {
return {
- isRTL: select( 'core/editor' ).getEditorSettings().isRTL,
+ isRTL: select( 'core/block-editor' ).getEditorSettings().isRTL,
};
} )( HoverArea );
diff --git a/packages/editor/src/components/block-list/index.js b/packages/editor/src/components/block-list/index.js
index 6ab8cd3956239..cc998be715c68 100644
--- a/packages/editor/src/components/block-list/index.js
+++ b/packages/editor/src/components/block-list/index.js
@@ -243,7 +243,7 @@ export default compose( [
getSelectedBlockClientId,
getMultiSelectedBlockClientIds,
hasMultiSelection,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const { rootClientId } = ownProps;
return {
@@ -262,7 +262,7 @@ export default compose( [
startMultiSelect,
stopMultiSelect,
multiSelect,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
return {
onStartMultiSelect: startMultiSelect,
diff --git a/packages/editor/src/components/block-list/insertion-point.js b/packages/editor/src/components/block-list/insertion-point.js
index f44f4b69d86e7..b0c7b5b270f16 100644
--- a/packages/editor/src/components/block-list/insertion-point.js
+++ b/packages/editor/src/components/block-list/insertion-point.js
@@ -85,7 +85,7 @@ export default withSelect( ( select, { clientId, rootClientId } ) => {
getBlockIndex,
getBlockInsertionPoint,
isBlockInsertionPointVisible,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const blockIndex = getBlockIndex( clientId, rootClientId );
const insertionPoint = getBlockInsertionPoint();
const showInsertionPoint = (
diff --git a/packages/editor/src/components/block-list/multi-controls.js b/packages/editor/src/components/block-list/multi-controls.js
index d00afc5a6af6d..5fb34625389ee 100644
--- a/packages/editor/src/components/block-list/multi-controls.js
+++ b/packages/editor/src/components/block-list/multi-controls.js
@@ -41,7 +41,7 @@ export default withSelect( ( select, { clientId } ) => {
isMultiSelecting,
getBlockIndex,
getBlockCount,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const clientIds = getMultiSelectedBlockClientIds();
const firstIndex = getBlockIndex( first( clientIds ), clientId );
const lastIndex = getBlockIndex( last( clientIds ), clientId );
diff --git a/packages/editor/src/components/block-mover/index.js b/packages/editor/src/components/block-mover/index.js
index 1996740feb40a..0ea8166cc279c 100644
--- a/packages/editor/src/components/block-mover/index.js
+++ b/packages/editor/src/components/block-mover/index.js
@@ -117,7 +117,7 @@ export class BlockMover extends Component {
export default compose(
withSelect( ( select, { clientIds } ) => {
- const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId } = select( 'core/editor' );
+ const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId } = select( 'core/block-editor' );
const firstClientId = first( castArray( clientIds ) );
const block = getBlock( firstClientId );
const rootClientId = getBlockRootClientId( first( castArray( clientIds ) ) );
@@ -130,7 +130,7 @@ export default compose(
};
} ),
withDispatch( ( dispatch, { clientIds, rootClientId } ) => {
- const { moveBlocksDown, moveBlocksUp } = dispatch( 'core/editor' );
+ const { moveBlocksDown, moveBlocksUp } = dispatch( 'core/block-editor' );
return {
onMoveDown: partial( moveBlocksDown, clientIds, rootClientId ),
onMoveUp: partial( moveBlocksUp, clientIds, rootClientId ),
diff --git a/packages/editor/src/components/block-navigation/dropdown.js b/packages/editor/src/components/block-navigation/dropdown.js
index bfcb5f3f0e2d3..104279fc877b1 100644
--- a/packages/editor/src/components/block-navigation/dropdown.js
+++ b/packages/editor/src/components/block-navigation/dropdown.js
@@ -50,7 +50,7 @@ function BlockNavigationDropdown( { hasBlocks, isTextModeEnabled } ) {
export default withSelect( ( select ) => {
return {
- hasBlocks: !! select( 'core/editor' ).getBlockCount(),
+ hasBlocks: !! select( 'core/block-editor' ).getBlockCount(),
isTextModeEnabled: select( 'core/edit-post' ).getEditorMode() === 'text',
};
} )( BlockNavigationDropdown );
diff --git a/packages/editor/src/components/block-navigation/index.js b/packages/editor/src/components/block-navigation/index.js
index 98a55c38af7d7..a3f67764fa36a 100644
--- a/packages/editor/src/components/block-navigation/index.js
+++ b/packages/editor/src/components/block-navigation/index.js
@@ -110,7 +110,7 @@ export default compose(
getBlockHierarchyRootClientId,
getBlock,
getBlocks,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
return {
rootBlocks: getBlocks(),
@@ -121,7 +121,7 @@ export default compose(
withDispatch( ( dispatch, { onSelect = noop } ) => {
return {
selectBlock( clientId ) {
- dispatch( 'core/editor' ).selectBlock( clientId );
+ dispatch( 'core/block-editor' ).selectBlock( clientId );
onSelect( clientId );
},
};
diff --git a/packages/editor/src/components/block-selection-clearer/index.js b/packages/editor/src/components/block-selection-clearer/index.js
index 7e092163d81b8..be39e5df9e7de 100644
--- a/packages/editor/src/components/block-selection-clearer/index.js
+++ b/packages/editor/src/components/block-selection-clearer/index.js
@@ -60,7 +60,7 @@ class BlockSelectionClearer extends Component {
export default compose( [
withSelect( ( select ) => {
- const { hasSelectedBlock, hasMultiSelection } = select( 'core/editor' );
+ const { hasSelectedBlock, hasMultiSelection } = select( 'core/block-editor' );
return {
hasSelectedBlock: hasSelectedBlock(),
@@ -68,7 +68,7 @@ export default compose( [
};
} ),
withDispatch( ( dispatch ) => {
- const { clearSelectedBlock } = dispatch( 'core/editor' );
+ const { clearSelectedBlock } = dispatch( 'core/block-editor' );
return { clearSelectedBlock };
} ),
] )( BlockSelectionClearer );
diff --git a/packages/editor/src/components/block-settings-menu/block-html-convert-button.js b/packages/editor/src/components/block-settings-menu/block-html-convert-button.js
index 2e49c011e6d7f..3527d7d0d460c 100644
--- a/packages/editor/src/components/block-settings-menu/block-html-convert-button.js
+++ b/packages/editor/src/components/block-settings-menu/block-html-convert-button.js
@@ -12,7 +12,7 @@ import BlockConvertButton from './block-convert-button';
export default compose(
withSelect( ( select, { clientId } ) => {
- const block = select( 'core/editor' ).getBlock( clientId );
+ const block = select( 'core/block-editor' ).getBlock( clientId );
return {
block,
@@ -20,7 +20,7 @@ export default compose(
};
} ),
withDispatch( ( dispatch, { block } ) => ( {
- onClick: () => dispatch( 'core/editor' ).replaceBlocks(
+ onClick: () => dispatch( 'core/block-editor' ).replaceBlocks(
block.clientId,
rawHandler( { HTML: getBlockContent( block ) } ),
),
diff --git a/packages/editor/src/components/block-settings-menu/block-mode-toggle.js b/packages/editor/src/components/block-settings-menu/block-mode-toggle.js
index a8702f58f2574..ffafcffff75f5 100644
--- a/packages/editor/src/components/block-settings-menu/block-mode-toggle.js
+++ b/packages/editor/src/components/block-settings-menu/block-mode-toggle.js
@@ -35,7 +35,7 @@ export function BlockModeToggle( { blockType, mode, onToggleMode, small = false
export default compose( [
withSelect( ( select, { clientId } ) => {
- const { getBlock, getBlockMode } = select( 'core/editor' );
+ const { getBlock, getBlockMode } = select( 'core/block-editor' );
const block = getBlock( clientId );
return {
@@ -45,7 +45,7 @@ export default compose( [
} ),
withDispatch( ( dispatch, { onToggle = noop, clientId } ) => ( {
onToggleMode() {
- dispatch( 'core/editor' ).toggleBlockMode( clientId );
+ dispatch( 'core/block-editor' ).toggleBlockMode( clientId );
onToggle();
},
} ) ),
diff --git a/packages/editor/src/components/block-settings-menu/block-unknown-convert-button.js b/packages/editor/src/components/block-settings-menu/block-unknown-convert-button.js
index e2bc0d3f8e0b7..46eda45141ad1 100644
--- a/packages/editor/src/components/block-settings-menu/block-unknown-convert-button.js
+++ b/packages/editor/src/components/block-settings-menu/block-unknown-convert-button.js
@@ -12,7 +12,7 @@ import BlockConvertButton from './block-convert-button';
export default compose(
withSelect( ( select, { clientId } ) => {
- const block = select( 'core/editor' ).getBlock( clientId );
+ const block = select( 'core/block-editor' ).getBlock( clientId );
return {
block,
@@ -20,7 +20,7 @@ export default compose(
};
} ),
withDispatch( ( dispatch, { block } ) => ( {
- onClick: () => dispatch( 'core/editor' ).replaceBlocks(
+ onClick: () => dispatch( 'core/block-editor' ).replaceBlocks(
block.clientId,
rawHandler( { HTML: serialize( block ) } )
),
diff --git a/packages/editor/src/components/block-settings-menu/index.js b/packages/editor/src/components/block-settings-menu/index.js
index 8436276c8f99b..b07452bdce393 100644
--- a/packages/editor/src/components/block-settings-menu/index.js
+++ b/packages/editor/src/components/block-settings-menu/index.js
@@ -137,7 +137,7 @@ export function BlockSettingsMenu( { clientIds, onSelect } ) {
}
export default withDispatch( ( dispatch ) => {
- const { selectBlock } = dispatch( 'core/editor' );
+ const { selectBlock } = dispatch( 'core/block-editor' );
return {
onSelect( clientId ) {
diff --git a/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js b/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js
index 2e528a01e7d91..3ec5076891eec 100644
--- a/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js
+++ b/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js
@@ -52,6 +52,8 @@ export default compose( [
const {
getBlocksByClientId,
canInsertBlockType,
+ } = select( 'core/block-editor' );
+ const {
__experimentalGetReusableBlock: getReusableBlock,
} = select( 'core/editor' );
const { canUser } = select( 'core' );
diff --git a/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js b/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js
index 5a73912b36ee7..a6271874d6533 100644
--- a/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js
+++ b/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js
@@ -31,12 +31,9 @@ export function ReusableBlockDeleteButton( { isVisible, isDisabled, onDelete } )
export default compose( [
withSelect( ( select, { clientId } ) => {
- const {
- getBlock,
- __experimentalGetReusableBlock: getReusableBlock,
- } = select( 'core/editor' );
+ const { getBlock } = select( 'core/block-editor' );
const { canUser } = select( 'core' );
-
+ const { __experimentalGetReusableBlock: getReusableBlock } = select( 'core/editor' );
const block = getBlock( clientId );
const reusableBlock = block && isReusableBlock( block ) ?
@@ -52,7 +49,7 @@ export default compose( [
const {
__experimentalDeleteReusableBlock: deleteReusableBlock,
} = dispatch( 'core/editor' );
- const { getBlock } = select( 'core/editor' );
+ const { getBlock } = select( 'core/block-editor' );
return {
onDelete() {
diff --git a/packages/editor/src/components/block-styles/index.js b/packages/editor/src/components/block-styles/index.js
index c44f8f686e191..ae7da2d26a559 100644
--- a/packages/editor/src/components/block-styles/index.js
+++ b/packages/editor/src/components/block-styles/index.js
@@ -143,7 +143,7 @@ function BlockStyles( {
export default compose( [
withSelect( ( select, { clientId } ) => {
- const { getBlock } = select( 'core/editor' );
+ const { getBlock } = select( 'core/block-editor' );
const { getBlockStyles } = select( 'core/blocks' );
const block = getBlock( clientId );
const blockType = getBlockType( block.name );
@@ -159,7 +159,7 @@ export default compose( [
withDispatch( ( dispatch, { clientId } ) => {
return {
onChangeClassName( newClassName ) {
- dispatch( 'core/editor' ).updateBlockAttributes( clientId, {
+ dispatch( 'core/block-editor' ).updateBlockAttributes( clientId, {
className: newClassName,
} );
},
diff --git a/packages/editor/src/components/block-switcher/index.js b/packages/editor/src/components/block-switcher/index.js
index 4a7413fb36422..25ef98edf8230 100644
--- a/packages/editor/src/components/block-switcher/index.js
+++ b/packages/editor/src/components/block-switcher/index.js
@@ -175,7 +175,7 @@ export class BlockSwitcher extends Component {
export default compose(
withSelect( ( select, { clientIds } ) => {
- const { getBlocksByClientId, getBlockRootClientId, getInserterItems } = select( 'core/editor' );
+ const { getBlocksByClientId, getBlockRootClientId, getInserterItems } = select( 'core/block-editor' );
const { getBlockStyles } = select( 'core/blocks' );
const rootClientId = getBlockRootClientId( first( castArray( clientIds ) ) );
const blocks = getBlocksByClientId( clientIds );
@@ -189,7 +189,7 @@ export default compose(
} ),
withDispatch( ( dispatch, ownProps ) => ( {
onTransform( blocks, name ) {
- dispatch( 'core/editor' ).replaceBlocks(
+ dispatch( 'core/block-editor' ).replaceBlocks(
ownProps.clientIds,
switchToBlockType( blocks, name )
);
diff --git a/packages/editor/src/components/block-switcher/multi-blocks-switcher.js b/packages/editor/src/components/block-switcher/multi-blocks-switcher.js
index 25d9e8c339e7a..8adae420dab99 100644
--- a/packages/editor/src/components/block-switcher/multi-blocks-switcher.js
+++ b/packages/editor/src/components/block-switcher/multi-blocks-switcher.js
@@ -19,7 +19,7 @@ export function MultiBlocksSwitcher( { isMultiBlockSelection, selectedBlockClien
export default withSelect(
( select ) => {
- const selectedBlockClientIds = select( 'core/editor' ).getMultiSelectedBlockClientIds();
+ const selectedBlockClientIds = select( 'core/block-editor' ).getMultiSelectedBlockClientIds();
return {
isMultiBlockSelection: selectedBlockClientIds.length > 1,
selectedBlockClientIds,
diff --git a/packages/editor/src/components/block-title/index.js b/packages/editor/src/components/block-title/index.js
index 6629c16768437..99519655419e1 100644
--- a/packages/editor/src/components/block-title/index.js
+++ b/packages/editor/src/components/block-title/index.js
@@ -32,7 +32,7 @@ export function BlockTitle( { name } ) {
}
export default withSelect( ( select, ownProps ) => {
- const { getBlockName } = select( 'core/editor' );
+ const { getBlockName } = select( 'core/block-editor' );
const { clientId } = ownProps;
return {
diff --git a/packages/editor/src/components/block-toolbar/index.js b/packages/editor/src/components/block-toolbar/index.js
index 3da857dbd1b99..2c3fa018731e9 100644
--- a/packages/editor/src/components/block-toolbar/index.js
+++ b/packages/editor/src/components/block-toolbar/index.js
@@ -47,7 +47,7 @@ export default withSelect( ( select ) => {
getBlockMode,
getMultiSelectedBlockClientIds,
isBlockValid,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
const blockClientIds = selectedBlockClientId ?
[ selectedBlockClientId ] :
diff --git a/packages/editor/src/components/color-palette/with-color-context.js b/packages/editor/src/components/color-palette/with-color-context.js
index 7112550219bbf..48eaa8085fca9 100644
--- a/packages/editor/src/components/color-palette/with-color-context.js
+++ b/packages/editor/src/components/color-palette/with-color-context.js
@@ -13,7 +13,7 @@ import { withSelect } from '@wordpress/data';
export default createHigherOrderComponent(
withSelect(
( select, ownProps ) => {
- const settings = select( 'core/editor' ).getEditorSettings();
+ const settings = select( 'core/block-editor' ).getEditorSettings();
const colors = ownProps.colors === undefined ?
settings.colors : ownProps.colors;
diff --git a/packages/editor/src/components/colors/with-colors.js b/packages/editor/src/components/colors/with-colors.js
index 3afb5e7d7469c..11524408b4a29 100644
--- a/packages/editor/src/components/colors/with-colors.js
+++ b/packages/editor/src/components/colors/with-colors.js
@@ -36,7 +36,7 @@ const withCustomColorPalette = ( colorsArray ) => createHigherOrderComponent( (
* @return {function} The higher order component.
*/
const withEditorColorPalette = () => withSelect( ( select ) => {
- const settings = select( 'core/editor' ).getEditorSettings();
+ const settings = select( 'core/block-editor' ).getEditorSettings();
return {
colors: get( settings, [ 'colors' ], DEFAULT_COLORS ),
};
diff --git a/packages/editor/src/components/copy-handler/index.js b/packages/editor/src/components/copy-handler/index.js
index 90d8324d033fc..3942eaae71b03 100644
--- a/packages/editor/src/components/copy-handler/index.js
+++ b/packages/editor/src/components/copy-handler/index.js
@@ -37,8 +37,8 @@ export default compose( [
getMultiSelectedBlockClientIds,
getSelectedBlockClientId,
hasMultiSelection,
- } = select( 'core/editor' );
- const { removeBlocks } = dispatch( 'core/editor' );
+ } = select( 'core/block-editor' );
+ const { removeBlocks } = dispatch( 'core/block-editor' );
const onCopy = ( event ) => {
const selectedBlockClientIds = getSelectedBlockClientId() ?
diff --git a/packages/editor/src/components/default-block-appender/index.js b/packages/editor/src/components/default-block-appender/index.js
index 594c357064de6..5a320ba4c105d 100644
--- a/packages/editor/src/components/default-block-appender/index.js
+++ b/packages/editor/src/components/default-block-appender/index.js
@@ -74,7 +74,7 @@ export function DefaultBlockAppender( {
export default compose(
withState( { hovered: false } ),
withSelect( ( select, ownProps ) => {
- const { getBlockCount, getBlockName, isBlockValid, getEditorSettings, getTemplateLock } = select( 'core/editor' );
+ const { getBlockCount, getBlockName, isBlockValid, getEditorSettings, getTemplateLock } = select( 'core/block-editor' );
const isEmpty = ! getBlockCount( ownProps.rootClientId );
const isLastBlockDefault = getBlockName( ownProps.lastBlockClientId ) === getDefaultBlockName();
@@ -92,7 +92,7 @@ export default compose(
const {
insertDefaultBlock,
startTyping,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
return {
onAppend() {
diff --git a/packages/editor/src/components/default-block-appender/index.native.js b/packages/editor/src/components/default-block-appender/index.native.js
index 8255afc3cd8fc..ab34b5ebbde21 100644
--- a/packages/editor/src/components/default-block-appender/index.native.js
+++ b/packages/editor/src/components/default-block-appender/index.native.js
@@ -49,7 +49,7 @@ export function DefaultBlockAppender( {
export default compose(
withSelect( ( select, ownProps ) => {
- const { getBlockCount, getEditorSettings, getTemplateLock } = select( 'core/editor' );
+ const { getBlockCount, getEditorSettings, getTemplateLock } = select( 'core/block-editor' );
const isEmpty = ! getBlockCount( ownProps.rootClientId );
const { bodyPlaceholder } = getEditorSettings();
@@ -64,7 +64,7 @@ export default compose(
const {
insertDefaultBlock,
startTyping,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
return {
onAppend() {
diff --git a/packages/editor/src/components/document-outline/check.js b/packages/editor/src/components/document-outline/check.js
index 80da12a58e882..ccbf66c27d150 100644
--- a/packages/editor/src/components/document-outline/check.js
+++ b/packages/editor/src/components/document-outline/check.js
@@ -19,5 +19,5 @@ function DocumentOutlineCheck( { blocks, children } ) {
}
export default withSelect( ( select ) => ( {
- blocks: select( 'core/editor' ).getBlocks(),
+ blocks: select( 'core/block-editor' ).getBlocks(),
} ) )( DocumentOutlineCheck );
diff --git a/packages/editor/src/components/document-outline/index.js b/packages/editor/src/components/document-outline/index.js
index 258bb792e7e3f..689144d040acc 100644
--- a/packages/editor/src/components/document-outline/index.js
+++ b/packages/editor/src/components/document-outline/index.js
@@ -140,7 +140,7 @@ export const DocumentOutline = ( { blocks = [], title, onSelect, isTitleSupporte
export default compose(
withSelect( ( select ) => {
- const { getEditedPostAttribute, getBlocks } = select( 'core/editor' );
+ const { getEditedPostAttribute, getBlocks } = select( 'core/block-editor' );
const { getPostType } = select( 'core' );
const postType = getPostType( getEditedPostAttribute( 'type' ) );
@@ -151,7 +151,7 @@ export default compose(
};
} ),
withDispatch( ( dispatch ) => {
- const { selectBlock } = dispatch( 'core/editor' );
+ const { selectBlock } = dispatch( 'core/block-editor' );
return {
onSelect: selectBlock,
};
diff --git a/packages/editor/src/components/font-sizes/font-size-picker.js b/packages/editor/src/components/font-sizes/font-size-picker.js
index 20e3fea9c2ad0..6989bb2233669 100644
--- a/packages/editor/src/components/font-sizes/font-size-picker.js
+++ b/packages/editor/src/components/font-sizes/font-size-picker.js
@@ -9,7 +9,7 @@ export default withSelect(
const {
disableCustomFontSizes,
fontSizes,
- } = select( 'core/editor' ).getEditorSettings();
+ } = select( 'core/block-editor' ).getEditorSettings();
return {
disableCustomFontSizes,
diff --git a/packages/editor/src/components/font-sizes/with-font-sizes.js b/packages/editor/src/components/font-sizes/with-font-sizes.js
index 3c9be5c1a4440..39953baf801f2 100644
--- a/packages/editor/src/components/font-sizes/with-font-sizes.js
+++ b/packages/editor/src/components/font-sizes/with-font-sizes.js
@@ -38,7 +38,7 @@ export default ( ...fontSizeNames ) => {
return createHigherOrderComponent(
compose( [
withSelect( ( select ) => {
- const { fontSizes } = select( 'core/editor' ).getEditorSettings();
+ const { fontSizes } = select( 'core/block-editor' ).getEditorSettings();
return {
fontSizes,
};
diff --git a/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js b/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js
index c2a117ec6816f..826915136888c 100644
--- a/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js
+++ b/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js
@@ -29,9 +29,7 @@ export default compose( [
};
} ),
withDispatch( ( dispatch, ownProps, { select } ) => {
- const {
- savePost,
- } = dispatch( 'core/editor' );
+ const { savePost } = dispatch( 'core/editor' );
return {
onSave() {
diff --git a/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js b/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js
index 9242734d0e9e4..02fe1d0e94b7e 100644
--- a/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js
+++ b/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js
@@ -146,7 +146,7 @@ const EnhancedVisualEditorGlobalKeyboardShortcuts = compose( [
getBlockRootClientId,
getTemplateLock,
getSelectedBlockClientId,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
const selectedBlockClientIds = selectedBlockClientId ? [ selectedBlockClientId ] : getMultiSelectedBlockClientIds();
@@ -161,12 +161,16 @@ const EnhancedVisualEditorGlobalKeyboardShortcuts = compose( [
};
} ),
withDispatch( ( dispatch ) => {
+ // This component should probably be split into to
+ // A block editor specific one and a post editor one.
const {
clearSelectedBlock,
multiSelect,
+ removeBlocks,
+ } = dispatch( 'core/block-editor' );
+ const {
redo,
undo,
- removeBlocks,
} = dispatch( 'core/editor' );
return {
diff --git a/packages/editor/src/components/inner-blocks/index.js b/packages/editor/src/components/inner-blocks/index.js
index c4cb0ac5d4099..eba0122e68856 100644
--- a/packages/editor/src/components/inner-blocks/index.js
+++ b/packages/editor/src/components/inner-blocks/index.js
@@ -132,7 +132,7 @@ InnerBlocks = compose( [
getBlockListSettings,
getBlockRootClientId,
getTemplateLock,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const { clientId } = ownProps;
const rootClientId = getBlockRootClientId( clientId );
return {
@@ -147,7 +147,7 @@ InnerBlocks = compose( [
replaceBlocks,
insertBlocks,
updateBlockListSettings,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
const { block, clientId, templateInsertUpdatesSelection = true } = ownProps;
return {
diff --git a/packages/editor/src/components/inserter-with-shortcuts/index.js b/packages/editor/src/components/inserter-with-shortcuts/index.js
index 935a7fa67095c..8e64fc6eaa580 100644
--- a/packages/editor/src/components/inserter-with-shortcuts/index.js
+++ b/packages/editor/src/components/inserter-with-shortcuts/index.js
@@ -49,7 +49,7 @@ function InserterWithShortcuts( { items, isLocked, onInsert } ) {
export default compose(
withSelect( ( select, { rootClientId } ) => {
- const { getInserterItems, getTemplateLock } = select( 'core/editor' );
+ const { getInserterItems, getTemplateLock } = select( 'core/block-editor' );
return {
items: getInserterItems( rootClientId ),
isLocked: !! getTemplateLock( rootClientId ),
@@ -62,9 +62,9 @@ export default compose(
onInsert( { name, initialAttributes } ) {
const block = createBlock( name, initialAttributes );
if ( clientId ) {
- dispatch( 'core/editor' ).replaceBlocks( clientId, block );
+ dispatch( 'core/block-editor' ).replaceBlocks( clientId, block );
} else {
- dispatch( 'core/editor' ).insertBlock( block, undefined, rootClientId );
+ dispatch( 'core/block-editor' ).insertBlock( block, undefined, rootClientId );
}
},
};
diff --git a/packages/editor/src/components/inserter/child-blocks.js b/packages/editor/src/components/inserter/child-blocks.js
index 312e2cc69ebe9..832c1e2b73038 100644
--- a/packages/editor/src/components/inserter/child-blocks.js
+++ b/packages/editor/src/components/inserter/child-blocks.js
@@ -32,7 +32,7 @@ export default compose(
} = select( 'core/blocks' );
const {
getBlockName,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const rootBlockName = getBlockName( rootClientId );
const rootBlockType = getBlockType( rootBlockName );
return {
diff --git a/packages/editor/src/components/inserter/index.js b/packages/editor/src/components/inserter/index.js
index 8c181f3cbd24c..4dfd3303fd4e5 100644
--- a/packages/editor/src/components/inserter/index.js
+++ b/packages/editor/src/components/inserter/index.js
@@ -103,8 +103,13 @@ class Inserter extends Component {
export default compose( [
withSelect( ( select, { rootClientId } ) => {
const {
- getEditedPostAttribute,
hasInserterItems,
+ } = select( 'core/block-editor' );
+
+ // The title should be removed from the inserter
+ // or replaced by a prop passed to the inserter.
+ const {
+ getEditedPostAttribute,
} = select( 'core/editor' );
return {
diff --git a/packages/editor/src/components/inserter/menu.js b/packages/editor/src/components/inserter/menu.js
index 7fb3cb2ffdca6..023a4b1102fe5 100644
--- a/packages/editor/src/components/inserter/menu.js
+++ b/packages/editor/src/components/inserter/menu.js
@@ -349,7 +349,7 @@ export default compose(
const {
getInserterItems,
getBlockName,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const {
getChildBlockNames,
} = select( 'core/blocks' );
@@ -364,9 +364,13 @@ export default compose(
} ),
withDispatch( ( dispatch, ownProps, { select } ) => {
const {
- __experimentalFetchReusableBlocks: fetchReusableBlocks,
showInsertionPoint,
hideInsertionPoint,
+ } = dispatch( 'core/block-editor' );
+
+ // This should be an external action provided in the editor settings.
+ const {
+ __experimentalFetchReusableBlocks: fetchReusableBlocks,
} = dispatch( 'core/editor' );
// To avoid duplication, getInsertionPoint is extracted and used in two event handlers
@@ -380,7 +384,7 @@ export default compose(
getBlockRootClientId,
getBlockSelectionEnd,
getBlockOrder,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const { clientId, rootClientId, isAppender } = ownProps;
// If the clientId is defined, we insert at the position of the block.
@@ -419,10 +423,10 @@ export default compose(
const {
replaceBlocks,
insertBlock,
- } = dispatch( 'core/editor' );
+ } = dispatch( 'core/block-editor' );
const {
getSelectedBlock,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const { isAppender } = ownProps;
const { name, initialAttributes } = item;
const selectedBlock = getSelectedBlock();
diff --git a/packages/editor/src/components/multi-select-scroll-into-view/index.js b/packages/editor/src/components/multi-select-scroll-into-view/index.js
index b616e370d0287..e49a90a520cbe 100644
--- a/packages/editor/src/components/multi-select-scroll-into-view/index.js
+++ b/packages/editor/src/components/multi-select-scroll-into-view/index.js
@@ -58,7 +58,7 @@ class MultiSelectScrollIntoView extends Component {
}
export default withSelect( ( select ) => {
- const { getLastMultiSelectedBlockClientId } = select( 'core/editor' );
+ const { getLastMultiSelectedBlockClientId } = select( 'core/block-editor' );
return {
extentClientId: getLastMultiSelectedBlockClientId(),
diff --git a/packages/editor/src/components/multi-selection-inspector/index.js b/packages/editor/src/components/multi-selection-inspector/index.js
index 51b48333923a5..5032676ae3ab1 100644
--- a/packages/editor/src/components/multi-selection-inspector/index.js
+++ b/packages/editor/src/components/multi-selection-inspector/index.js
@@ -42,7 +42,7 @@ function MultiSelectionInspector( { blocks } ) {
}
export default withSelect( ( select ) => {
- const { getMultiSelectedBlocks } = select( 'core/editor' );
+ const { getMultiSelectedBlocks } = select( 'core/block-editor' );
return {
blocks: getMultiSelectedBlocks(),
};
diff --git a/packages/editor/src/components/observe-typing/index.js b/packages/editor/src/components/observe-typing/index.js
index fa3f669da5410..a45931bcee0fd 100644
--- a/packages/editor/src/components/observe-typing/index.js
+++ b/packages/editor/src/components/observe-typing/index.js
@@ -201,14 +201,14 @@ class ObserveTyping extends Component {
export default compose( [
withSelect( ( select ) => {
- const { isTyping } = select( 'core/editor' );
+ const { isTyping } = select( 'core/block-editor' );
return {
isTyping: isTyping(),
};
} ),
withDispatch( ( dispatch ) => {
- const { startTyping, stopTyping } = dispatch( 'core/editor' );
+ const { startTyping, stopTyping } = dispatch( 'core/block-editor' );
return {
onStartTyping: startTyping,
diff --git a/packages/editor/src/components/page-attributes/check.js b/packages/editor/src/components/page-attributes/check.js
index 56be444a04985..014b2aa29136a 100644
--- a/packages/editor/src/components/page-attributes/check.js
+++ b/packages/editor/src/components/page-attributes/check.js
@@ -20,8 +20,11 @@ export function PageAttributesCheck( { availableTemplates, postType, children }
}
export default withSelect( ( select ) => {
- const { getEditedPostAttribute, getEditorSettings } = select( 'core/editor' );
+ const { getEditedPostAttribute } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
const { getPostType } = select( 'core' );
+
+ // This setting should not live in the block-editor module.
const { availableTemplates } = getEditorSettings();
return {
postType: getPostType( getEditedPostAttribute( 'type' ) ),
diff --git a/packages/editor/src/components/page-attributes/template.js b/packages/editor/src/components/page-attributes/template.js
index 3646a05e3206f..8e6d9cef689cd 100644
--- a/packages/editor/src/components/page-attributes/template.js
+++ b/packages/editor/src/components/page-attributes/template.js
@@ -33,7 +33,8 @@ export function PageTemplate( { availableTemplates, selectedTemplate, onUpdate }
export default compose(
withSelect( ( select ) => {
- const { getEditedPostAttribute, getEditorSettings } = select( 'core/editor' );
+ const { getEditedPostAttribute } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
const { availableTemplates } = getEditorSettings();
return {
selectedTemplate: getEditedPostAttribute( 'template' ),
diff --git a/packages/editor/src/components/post-format/check.js b/packages/editor/src/components/post-format/check.js
index cd124553a7cf1..d13e354655e9d 100644
--- a/packages/editor/src/components/post-format/check.js
+++ b/packages/editor/src/components/post-format/check.js
@@ -15,7 +15,8 @@ function PostFormatCheck( { disablePostFormats, ...props } ) {
export default withSelect(
( select ) => {
- const editorSettings = select( 'core/editor' ).getEditorSettings();
+ // This setting should not live in the block-editor's store.
+ const editorSettings = select( 'core/block-editor' ).getEditorSettings();
return {
disablePostFormats: editorSettings.disablePostFormats,
};
diff --git a/packages/editor/src/components/post-locked-modal/index.js b/packages/editor/src/components/post-locked-modal/index.js
index ecdb4b6a54b32..8a9b88cbc2109 100644
--- a/packages/editor/src/components/post-locked-modal/index.js
+++ b/packages/editor/src/components/post-locked-modal/index.js
@@ -214,7 +214,6 @@ class PostLockedModal extends Component {
export default compose(
withSelect( ( select ) => {
const {
- getEditorSettings,
isPostLocked,
isPostLockTakeover,
getPostLockUser,
@@ -222,12 +221,16 @@ export default compose(
getActivePostLock,
getEditedPostAttribute,
} = select( 'core/editor' );
+ const {
+ getEditorSettings,
+ } = select( 'core/block-editor' );
const { getPostType } = select( 'core' );
return {
isLocked: isPostLocked(),
isTakeover: isPostLockTakeover(),
user: getPostLockUser(),
postId: getCurrentPostId(),
+ // This setting should not live in the block-editor's store.
postLockUtils: getEditorSettings().postLockUtils,
activePostLock: getActivePostLock(),
postType: getPostType( getEditedPostAttribute( 'type' ) ),
diff --git a/packages/editor/src/components/post-title/index.js b/packages/editor/src/components/post-title/index.js
index 9adb9e5c9b64a..761fcff095df4 100644
--- a/packages/editor/src/components/post-title/index.js
+++ b/packages/editor/src/components/post-title/index.js
@@ -149,7 +149,8 @@ class PostTitle extends Component {
}
const applyWithSelect = withSelect( ( select ) => {
- const { getEditedPostAttribute, getEditorSettings, isCleanNewPost } = select( 'core/editor' );
+ const { getEditedPostAttribute, isCleanNewPost } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
const { getPostType } = select( 'core' );
const postType = getPostType( getEditedPostAttribute( 'type' ) );
const { titlePlaceholder, focusMode, hasFixedToolbar } = getEditorSettings();
@@ -167,8 +168,10 @@ const applyWithSelect = withSelect( ( select ) => {
const applyWithDispatch = withDispatch( ( dispatch ) => {
const {
insertDefaultBlock,
- editPost,
clearSelectedBlock,
+ } = dispatch( 'core/block-editor' );
+ const {
+ editPost,
undo,
redo,
} = dispatch( 'core/editor' );
diff --git a/packages/editor/src/components/post-title/index.native.js b/packages/editor/src/components/post-title/index.native.js
index 8204220b3be6d..55e56d03a101d 100644
--- a/packages/editor/src/components/post-title/index.native.js
+++ b/packages/editor/src/components/post-title/index.native.js
@@ -106,12 +106,15 @@ class PostTitle extends Component {
const applyWithDispatch = withDispatch( ( dispatch ) => {
const {
- insertDefaultBlock,
- clearSelectedBlock,
undo,
redo,
} = dispatch( 'core/editor' );
+ const {
+ insertDefaultBlock,
+ clearSelectedBlock,
+ } = dispatch( 'core/block-editor' );
+
return {
onEnterPress() {
insertDefaultBlock( undefined, undefined, 0 );
diff --git a/packages/editor/src/components/preserve-scroll-in-reorder/index.js b/packages/editor/src/components/preserve-scroll-in-reorder/index.js
index 25a28bff45d40..e791dea222655 100644
--- a/packages/editor/src/components/preserve-scroll-in-reorder/index.js
+++ b/packages/editor/src/components/preserve-scroll-in-reorder/index.js
@@ -77,7 +77,7 @@ class PreserveScrollInReorder extends Component {
export default withSelect( ( select ) => {
return {
- blockOrder: select( 'core/editor' ).getBlockOrder(),
- selectionStart: select( 'core/editor' ).getBlockSelectionStart(),
+ blockOrder: select( 'core/block-editor' ).getBlockOrder(),
+ selectionStart: select( 'core/block-editor' ).getBlockSelectionStart(),
};
} )( PreserveScrollInReorder );
diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js
index d086cf24795fb..ccfe32326d944 100644
--- a/packages/editor/src/components/rich-text/index.js
+++ b/packages/editor/src/components/rich-text/index.js
@@ -1104,7 +1104,9 @@ const RichTextContainer = compose( [
};
} ),
withSelect( ( select ) => {
- const { canUserUseUnfilteredHTML, isCaretWithinFormattedText } = select( 'core/editor' );
+ // This should probably be moved to the block editor settings.
+ const { canUserUseUnfilteredHTML } = select( 'core/editor' );
+ const { isCaretWithinFormattedText } = select( 'core/block-editor' );
const { getFormatTypes } = select( 'core/rich-text' );
return {
diff --git a/packages/editor/src/components/skip-to-selected-block/index.js b/packages/editor/src/components/skip-to-selected-block/index.js
index 7fb58d88b59da..419d94b537344 100644
--- a/packages/editor/src/components/skip-to-selected-block/index.js
+++ b/packages/editor/src/components/skip-to-selected-block/index.js
@@ -26,6 +26,6 @@ const SkipToSelectedBlock = ( { selectedBlockClientId } ) => {
export default withSelect( ( select ) => {
return {
- selectedBlockClientId: select( 'core/editor' ).getBlockSelectionStart(),
+ selectedBlockClientId: select( 'core/block-editor' ).getBlockSelectionStart(),
};
} )( SkipToSelectedBlock );
diff --git a/packages/editor/src/components/table-of-contents/index.js b/packages/editor/src/components/table-of-contents/index.js
index 473cc5cd5a1aa..af398e060085c 100644
--- a/packages/editor/src/components/table-of-contents/index.js
+++ b/packages/editor/src/components/table-of-contents/index.js
@@ -33,6 +33,6 @@ function TableOfContents( { hasBlocks } ) {
export default withSelect( ( select ) => {
return {
- hasBlocks: !! select( 'core/editor' ).getBlockCount(),
+ hasBlocks: !! select( 'core/block-editor' ).getBlockCount(),
};
} )( TableOfContents );
diff --git a/packages/editor/src/components/table-of-contents/panel.js b/packages/editor/src/components/table-of-contents/panel.js
index e9f2cc188fd4e..00489ccaf1e1d 100644
--- a/packages/editor/src/components/table-of-contents/panel.js
+++ b/packages/editor/src/components/table-of-contents/panel.js
@@ -57,7 +57,7 @@ function TableOfContentsPanel( { headingCount, paragraphCount, numberOfBlocks }
}
export default withSelect( ( select ) => {
- const { getGlobalBlockCount } = select( 'core/editor' );
+ const { getGlobalBlockCount } = select( 'core/block-editor' );
return {
headingCount: getGlobalBlockCount( 'core/heading' ),
paragraphCount: getGlobalBlockCount( 'core/paragraph' ),
diff --git a/packages/editor/src/components/template-validation-notice/index.js b/packages/editor/src/components/template-validation-notice/index.js
index f8ada453dc3f4..f9b612b3e7039 100644
--- a/packages/editor/src/components/template-validation-notice/index.js
+++ b/packages/editor/src/components/template-validation-notice/index.js
@@ -31,10 +31,10 @@ function TemplateValidationNotice( { isValid, ...props } ) {
export default compose( [
withSelect( ( select ) => ( {
- isValid: select( 'core/editor' ).isValidTemplate(),
+ isValid: select( 'core/block-editor' ).isValidTemplate(),
} ) ),
withDispatch( ( dispatch ) => {
- const { setTemplateValidity, synchronizeTemplate } = dispatch( 'core/editor' );
+ const { setTemplateValidity, synchronizeTemplate } = dispatch( 'core/block-editor' );
return {
resetTemplateValidity: () => setTemplateValidity( true ),
synchronizeTemplate,
diff --git a/packages/editor/src/components/writing-flow/index.js b/packages/editor/src/components/writing-flow/index.js
index 0d4ea96eaa7c4..12ea7da18f2e2 100644
--- a/packages/editor/src/components/writing-flow/index.js
+++ b/packages/editor/src/components/writing-flow/index.js
@@ -356,7 +356,7 @@ export default compose( [
getLastMultiSelectedBlockClientId,
hasMultiSelection,
getBlockOrder,
- } = select( 'core/editor' );
+ } = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
const selectionStartClientId = getMultiSelectedBlocksStartClientId();
@@ -374,7 +374,7 @@ export default compose( [
};
} ),
withDispatch( ( dispatch ) => {
- const { multiSelect, selectBlock } = dispatch( 'core/editor' );
+ const { multiSelect, selectBlock } = dispatch( 'core/block-editor' );
return {
onMultiSelect: multiSelect,
onSelectBlock: selectBlock,
diff --git a/packages/editor/src/hooks/align.js b/packages/editor/src/hooks/align.js
index e22922c198b2c..b9d2b842cef1d 100644
--- a/packages/editor/src/hooks/align.js
+++ b/packages/editor/src/hooks/align.js
@@ -168,7 +168,7 @@ export const withDataAlign = createHigherOrderComponent(
compose( [
withSelect(
( select ) => {
- const { getEditorSettings } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
return {
hasWideEnabled: !! getEditorSettings().alignWide,
};
diff --git a/packages/editor/src/utils/media-upload/index.js b/packages/editor/src/utils/media-upload/index.js
index 12e0335033247..ced9e70c6b253 100644
--- a/packages/editor/src/utils/media-upload/index.js
+++ b/packages/editor/src/utils/media-upload/index.js
@@ -33,10 +33,10 @@ export default function( {
onError = noop,
onFileChange,
} ) {
- const {
- getCurrentPostId,
- getEditorSettings,
- } = select( 'core/editor' );
+ const { getCurrentPostId } = select( 'core/editor' );
+ const { getEditorSettings } = select( 'core/block-editor' );
+
+ // These settings should not live in the block editor's store.
const wpAllowedMimeTypes = getEditorSettings().allowedMimeTypes;
maxUploadFileSize = maxUploadFileSize || getEditorSettings().maxUploadFileSize;
From 1919f839c011d310dcff4002d540394a9c425207 Mon Sep 17 00:00:00 2001
From: Miguel Fonseca
Date: Mon, 25 Feb 2019 14:47:38 +0000
Subject: [PATCH 023/169] Docs: release.md: miscellaneous fixes (#14083)
* Docs: release.md: misc. fixes
---
docs/contributors/release.md | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/docs/contributors/release.md b/docs/contributors/release.md
index b59a5472167f3..2321e63b70b42 100644
--- a/docs/contributors/release.md
+++ b/docs/contributors/release.md
@@ -2,7 +2,7 @@
This Repository is used to perform several types of releases. This document serves as a checklist for each one of these. It is helpful if you'd like to understand the different workflows.
-To release Gutenberg, you need commit access to the [WordPress.org plugin repository]. 🙂
+To release Gutenberg, you need commit access to the [WordPress.org plugin repository][plugin repository]. 🙂
## Plugin Releases
@@ -192,22 +192,23 @@ WordPress Core Updates are based on the `g-minor` branch. Releasing packages in
For major WordPress releases, the last Gutenberg plugin release is merged into `g-minor`. This involves the following steps:
-1. Checkout the last published Gutenberg's release branch `git checkout release/x.x`
+1. Check out the last published Gutenberg release branch `git checkout release/x.x`
2. Create a Pull Request from this branch into `g-minor`.
3. Merge the branch.
### Minor WordPress Releases
-For minor releases, the critical fixes targetted for this WordPress Minor release should be cherry-picked into the `g-minor` branch one by one in their chronological order.
+For minor releases, the critical fixes targeted for this WordPress Minor release should be cherry-picked into the `g-minor` branch one by one in their chronological order.
### Releasing the WordPress packages
-1. Checkout the `g-minor` branch.
-2. Run [the package release process](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md#releasing-packages)
-3. Update the `CHANGELOG` files of the published packages with the new released versions and commit to the `g-minor` branch.
+1. Check out the `g-minor` branch.
+2. Run the [package release process].
+3. Update the `CHANGELOG.md` files of the published packages with the new released versions and commit to the `g-minor` branch.
---------
Ta-da! 🎉
[plugin repository]: https://plugins.trac.wordpress.org/browser/gutenberg/
+[package release process]: https://github.com/WordPress/gutenberg/blob/master/packages/README.md#releasing-packages
From c8c1381f77c0cf1a2d67b96e5b612db2c4291395 Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Mon, 25 Feb 2019 16:15:02 +0100
Subject: [PATCH 024/169] Disable block navigation and document outline items
in text mode (#14081)
* Use aria-disabled instead of disabled.
* Fix unrecognized prop warning.
* Disable Document Outline items in text mode.
* Improve buttons alignment.
* Pass isTextModeEnabled as prop from parent.
---
.../src/components/header/header-toolbar/index.js | 7 ++++---
.../src/components/block-navigation/dropdown.js | 11 ++++++-----
.../editor/src/components/block-navigation/index.js | 3 +--
.../editor/src/components/document-outline/index.js | 4 +++-
.../editor/src/components/document-outline/item.js | 6 ++++--
.../editor/src/components/document-outline/style.scss | 10 ++++++++++
.../editor/src/components/table-of-contents/index.js | 4 ++--
.../editor/src/components/table-of-contents/panel.js | 4 ++--
8 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js
index 87a24a85e2a8a..7740b3b157a99 100644
--- a/packages/edit-post/src/components/header/header-toolbar/index.js
+++ b/packages/edit-post/src/components/header/header-toolbar/index.js
@@ -21,7 +21,7 @@ import {
*/
import FullscreenModeClose from '../fullscreen-mode-close';
-function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter } ) {
+function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter, isTextModeEnabled } ) {
const toolbarAriaLabel = hasFixedToolbar ?
/* translators: accessibility text for the editor toolbar when Top Toolbar is on */
__( 'Document and block tools' ) :
@@ -42,8 +42,8 @@ function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter } ) {
-
-
+
+
{ hasFixedToolbar && isLargeViewport && (
@@ -58,6 +58,7 @@ export default compose( [
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
// This setting (richEditingEnabled) should not live in the block editor's setting.
showInserter: select( 'core/edit-post' ).getEditorMode() === 'visual' && select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
+ isTextModeEnabled: select( 'core/edit-post' ).getEditorMode() === 'text',
} ) ),
withViewportMatch( { isLargeViewport: 'medium' } ),
] )( HeaderToolbar );
diff --git a/packages/editor/src/components/block-navigation/dropdown.js b/packages/editor/src/components/block-navigation/dropdown.js
index 104279fc877b1..35db5d9b2d1f2 100644
--- a/packages/editor/src/components/block-navigation/dropdown.js
+++ b/packages/editor/src/components/block-navigation/dropdown.js
@@ -18,12 +18,14 @@ const MenuIcon = (
);
-function BlockNavigationDropdown( { hasBlocks, isTextModeEnabled } ) {
+function BlockNavigationDropdown( { hasBlocks, isDisabled } ) {
+ const isEnabled = hasBlocks && ! isDisabled;
+
return (
(
- { hasBlocks && ! isTextModeEnabled &&
) }
@@ -51,6 +53,5 @@ function BlockNavigationDropdown( { hasBlocks, isTextModeEnabled } ) {
export default withSelect( ( select ) => {
return {
hasBlocks: !! select( 'core/block-editor' ).getBlockCount(),
- isTextModeEnabled: select( 'core/edit-post' ).getEditorMode() === 'text',
};
} )( BlockNavigationDropdown );
diff --git a/packages/editor/src/components/block-navigation/index.js b/packages/editor/src/components/block-navigation/index.js
index a3f67764fa36a..be7206d6cac0e 100644
--- a/packages/editor/src/components/block-navigation/index.js
+++ b/packages/editor/src/components/block-navigation/index.js
@@ -40,10 +40,9 @@ function BlockNavigationList( {
selectBlock( block.clientId ) }
- isSelected={ isSelected }
>
{ blockType.title }
diff --git a/packages/editor/src/components/document-outline/index.js b/packages/editor/src/components/document-outline/index.js
index 689144d040acc..63349419e43b6 100644
--- a/packages/editor/src/components/document-outline/index.js
+++ b/packages/editor/src/components/document-outline/index.js
@@ -64,7 +64,7 @@ const computeOutlineHeadings = ( blocks = [], path = [] ) => {
const isEmptyHeading = ( heading ) => ! heading.attributes.content || heading.attributes.content.length === 0;
-export const DocumentOutline = ( { blocks = [], title, onSelect, isTitleSupported } ) => {
+export const DocumentOutline = ( { blocks = [], title, onSelect, isTitleSupported, hasOutlineItemsDisabled } ) => {
const headings = computeOutlineHeadings( blocks );
if ( headings.length < 1 ) {
@@ -96,6 +96,7 @@ export const DocumentOutline = ( { blocks = [], title, onSelect, isTitleSupporte
level={ __( 'Title' ) }
isValid
onClick={ focusTitle }
+ isDisabled={ hasOutlineItemsDisabled }
>
{ title }
@@ -120,6 +121,7 @@ export const DocumentOutline = ( { blocks = [], title, onSelect, isTitleSupporte
isValid={ isValid }
onClick={ () => onSelectHeading( item.clientId ) }
path={ item.path }
+ isDisabled={ hasOutlineItemsDisabled }
>
{ item.isEmpty ?
emptyHeadingContent :
diff --git a/packages/editor/src/components/document-outline/item.js b/packages/editor/src/components/document-outline/item.js
index 3db43383c5a5f..eb6b147056f67 100644
--- a/packages/editor/src/components/document-outline/item.js
+++ b/packages/editor/src/components/document-outline/item.js
@@ -18,6 +18,7 @@ const TableOfContentsItem = ( {
isValid,
level,
onClick,
+ isDisabled,
path = [],
} ) => (
{
@@ -49,7 +51,7 @@ const TableOfContentsItem = ( {
{ children }
- { __( '(Click to focus this heading)' ) }
+ { ! isDisabled && { __( '(Click to focus this heading)' ) } }
);
diff --git a/packages/editor/src/components/document-outline/style.scss b/packages/editor/src/components/document-outline/style.scss
index 336f53d196687..53008b91e6b3d 100644
--- a/packages/editor/src/components/document-outline/style.scss
+++ b/packages/editor/src/components/document-outline/style.scss
@@ -43,9 +43,15 @@
border: none;
display: flex;
align-items: flex-start;
+ margin: 0 0 0 -1px;
+ padding: 2px 5px 2px 1px;
color: $dark-gray-800;
text-align: left;
+ &:disabled {
+ cursor: default;
+ }
+
&:focus {
@include button-style__focus-active;
}
@@ -63,3 +69,7 @@
background: $alert-yellow;
}
}
+
+.document-outline__item-content {
+ padding: 1px 0;
+}
diff --git a/packages/editor/src/components/table-of-contents/index.js b/packages/editor/src/components/table-of-contents/index.js
index af398e060085c..767f51dc6c1af 100644
--- a/packages/editor/src/components/table-of-contents/index.js
+++ b/packages/editor/src/components/table-of-contents/index.js
@@ -10,7 +10,7 @@ import { withSelect } from '@wordpress/data';
*/
import TableOfContentsPanel from './panel';
-function TableOfContents( { hasBlocks } ) {
+function TableOfContents( { hasBlocks, hasOutlineItemsDisabled } ) {
return (
) }
- renderContent={ () => }
+ renderContent={ () => }
/>
);
}
diff --git a/packages/editor/src/components/table-of-contents/panel.js b/packages/editor/src/components/table-of-contents/panel.js
index 00489ccaf1e1d..a1a2a4b413055 100644
--- a/packages/editor/src/components/table-of-contents/panel.js
+++ b/packages/editor/src/components/table-of-contents/panel.js
@@ -11,7 +11,7 @@ import { withSelect } from '@wordpress/data';
import WordCount from '../word-count';
import DocumentOutline from '../document-outline';
-function TableOfContentsPanel( { headingCount, paragraphCount, numberOfBlocks } ) {
+function TableOfContentsPanel( { headingCount, paragraphCount, numberOfBlocks, hasOutlineItemsDisabled } ) {
return (
{ __( 'Document Outline' ) }
-
+
) }
From cd82d07a70c5485396427f7196e78ae5c2d3b284 Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Mon, 25 Feb 2019 20:04:18 +0100
Subject: [PATCH 025/169] Fix selector in document outline. (#14094)
* Fix selector in document outline.
* Import getBlocks from its new location.
---
packages/editor/src/components/document-outline/index.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/editor/src/components/document-outline/index.js b/packages/editor/src/components/document-outline/index.js
index 63349419e43b6..b73af4a92d087 100644
--- a/packages/editor/src/components/document-outline/index.js
+++ b/packages/editor/src/components/document-outline/index.js
@@ -142,7 +142,8 @@ export const DocumentOutline = ( { blocks = [], title, onSelect, isTitleSupporte
export default compose(
withSelect( ( select ) => {
- const { getEditedPostAttribute, getBlocks } = select( 'core/block-editor' );
+ const { getBlocks } = select( 'core/block-editor' );
+ const { getEditedPostAttribute } = select( 'core/editor' );
const { getPostType } = select( 'core' );
const postType = getPostType( getEditedPostAttribute( 'type' ) );
From 89573e83a5e3ab2f8fad26e2dde9f87e047831e7 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Tue, 26 Feb 2019 08:59:09 -0500
Subject: [PATCH 026/169] Plugin: Require includes for deprecated
`use_block_editor_for_` functions (#14096)
* Plugin: Fix 5.1.0 deprecated functions to correct plugin version
* Plugin: Require includes for deprecated `use_block_editor_for_` functions
---
lib/register.php | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/lib/register.php b/lib/register.php
index 16d5aa85b623e..9f86fdb58200b 100644
--- a/lib/register.php
+++ b/lib/register.php
@@ -25,16 +25,16 @@ function gutenberg_collect_meta_box_data() {
* Return whether the post can be edited in Gutenberg and by the current user.
*
* @since 0.5.0
- * @deprecated 5.0.0 use_block_editor_for_post
+ * @deprecated 5.1.0 use_block_editor_for_post
*
* @param int|WP_Post $post Post ID or WP_Post object.
* @return bool Whether the post can be edited with Gutenberg.
*/
function gutenberg_can_edit_post( $post ) {
- _deprecated_function( __FUNCTION__, '5.0.0', 'use_block_editor_for_post' );
+ _deprecated_function( __FUNCTION__, '5.1.0', 'use_block_editor_for_post' );
+ require_once ABSPATH . 'wp-admin/includes/post.php';
return use_block_editor_for_post( $post );
-
}
/**
@@ -44,14 +44,15 @@ function gutenberg_can_edit_post( $post ) {
* REST API, then the post cannot be edited in Gutenberg.
*
* @since 1.5.2
- * @deprecated 5.0.0 use_block_editor_for_post_type
+ * @deprecated 5.1.0 use_block_editor_for_post_type
*
* @param string $post_type The post type.
* @return bool Whether the post type can be edited with Gutenberg.
*/
function gutenberg_can_edit_post_type( $post_type ) {
- _deprecated_function( __FUNCTION__, '5.0.0', 'use_block_editor_for_post_type' );
+ _deprecated_function( __FUNCTION__, '5.1.0', 'use_block_editor_for_post_type' );
+ require_once ABSPATH . 'wp-admin/includes/post.php';
return use_block_editor_for_post_type( $post_type );
}
@@ -155,23 +156,23 @@ function gutenberg_bulk_post_updated_messages( $messages ) {
* Injects a hidden input in the edit form to propagate the information that classic editor is selected.
*
* @since 1.5.2
- * @deprecated 5.0.0
+ * @deprecated 5.1.0
*/
function gutenberg_remember_classic_editor_when_saving_posts() {
- _deprecated_function( __FUNCTION__, '5.0.0' );
+ _deprecated_function( __FUNCTION__, '5.1.0' );
}
/**
* Appends a query argument to the redirect url to make sure it gets redirected to the classic editor.
*
* @since 1.5.2
- * @deprecated 5.0.0
+ * @deprecated 5.1.0
*
* @param string $url Redirect url.
* @return string Redirect url.
*/
function gutenberg_redirect_to_classic_editor_when_saving_posts( $url ) {
- _deprecated_function( __FUNCTION__, '5.0.0' );
+ _deprecated_function( __FUNCTION__, '5.1.0' );
return $url;
}
@@ -181,13 +182,13 @@ function gutenberg_redirect_to_classic_editor_when_saving_posts( $url ) {
* the editor from which the user navigated.
*
* @since 1.5.2
- * @deprecated 5.0.0
+ * @deprecated 5.1.0
*
* @param string $url Edit url.
* @return string Edit url.
*/
function gutenberg_revisions_link_to_editor( $url ) {
- _deprecated_function( __FUNCTION__, '5.0.0' );
+ _deprecated_function( __FUNCTION__, '5.1.0' );
return $url;
}
From e0adbe0748ac3f1e7e7d261f7039231dc0aa0914 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Tue, 26 Feb 2019 08:59:54 -0500
Subject: [PATCH 027/169] Plugin: Update server blocks script to use core
equivalent function (#14097)
`gutenberg_prepare_blocks_for_js` was deprecated in Gutenberg 5.0
---
bin/get-server-blocks.php | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/bin/get-server-blocks.php b/bin/get-server-blocks.php
index 851dbccfab6e6..164fafd467db9 100755
--- a/bin/get-server-blocks.php
+++ b/bin/get-server-blocks.php
@@ -24,10 +24,10 @@
require_once ABSPATH . WPINC . '/functions.php';
wp_load_translations_early();
wp_set_lang_dir();
-require_once dirname( dirname( __FILE__ ) ) . '/lib/blocks.php';
-require_once dirname( dirname( __FILE__ ) ) . '/lib/class-wp-block-type-registry.php';
-require_once dirname( dirname( __FILE__ ) ) . '/lib/class-wp-block-type.php';
-require_once dirname( dirname( __FILE__ ) ) . '/lib/client-assets.php';
+require_once ABSPATH . WPINC . '/blocks.php';
+require_once ABSPATH . WPINC . '/class-wp-block-type-registry.php';
+require_once ABSPATH . WPINC . '/class-wp-block-type.php';
+require_once ABSPATH . '/wp-admin/includes/post.php';
// Register server-side code for individual blocks.
foreach ( glob( dirname( dirname( __FILE__ ) ) . '/packages/block-library/src/*/index.php' ) as $block_logic ) {
@@ -36,4 +36,4 @@
do_action( 'init' );
-echo json_encode( gutenberg_prepare_blocks_for_js() );
+echo json_encode( get_block_editor_server_block_settings() );
From 63832a2a5bce7df64330c747c39ccde8f90eaa2e Mon Sep 17 00:00:00 2001
From: Kjell Reigstad
Date: Tue, 26 Feb 2019 10:47:55 -0500
Subject: [PATCH 028/169] Correct visual error in the quote block icon (#14091)
Fixes #13659.
The current quote block icon appears to have some over-simplified edges. This replaces it with a crisper version.
---
packages/block-library/src/quote/index.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/block-library/src/quote/index.js b/packages/block-library/src/quote/index.js
index 6a2506eb5f2e0..43c380be09f3a 100644
--- a/packages/block-library/src/quote/index.js
+++ b/packages/block-library/src/quote/index.js
@@ -15,7 +15,7 @@ import {
RichText,
} from '@wordpress/editor';
import { join, split, create, toHTMLString } from '@wordpress/rich-text';
-import { G, Path, SVG } from '@wordpress/components';
+import { Path, SVG } from '@wordpress/components';
const ATTRIBUTE_QUOTE = 'value';
const ATTRIBUTE_CITATION = 'citation';
@@ -44,7 +44,7 @@ export const name = 'core/quote';
export const settings = {
title: __( 'Quote' ),
description: __( 'Give quoted text visual emphasis. "In quoting others, we cite ourselves." — Julio Cortázar' ),
- icon: ,
+ icon: ,
category: 'common',
keywords: [ __( 'blockquote' ) ],
From f8912c2075720f4f3b5d8478cafc2fd2143dc3b3 Mon Sep 17 00:00:00 2001
From: Kjell Reigstad
Date: Tue, 26 Feb 2019 11:07:47 -0500
Subject: [PATCH 029/169] Try: Add a subtle animation to the is-active
indicator for sidebar tabs (#13956)
* Add subtle animation to the is-active indicator for sidebar tabs
* Re-instate the empty border, to prevent browser defaults from kicking in.
* Remove extra 1px of empty space
* Focus state cleanup for tab buttons
Allow them to inherit the box shadow (used for the active tab)
Use the usual standard dotted outline instead of a solid one.
* Add a pseudoclass to ensure the active border appears in Windows High Contrast Mode.
* Fix typo.
This should match the value from `/packages/edit-post/src/components/sidebar/settings-header/style.scss`
---
assets/stylesheets/_mixins.scss | 4 ++--
.../sidebar/settings-header/style.scss | 19 +++++++++++++++----
.../src/components/sidebar/style.scss | 18 +++++++++++++++---
3 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/assets/stylesheets/_mixins.scss b/assets/stylesheets/_mixins.scss
index fa79974e2a112..31134cd44d09b 100644
--- a/assets/stylesheets/_mixins.scss
+++ b/assets/stylesheets/_mixins.scss
@@ -195,8 +195,8 @@
@mixin square-style__focus() {
color: $dark-gray-900;
- outline: 1px solid $dark-gray-300;
- box-shadow: none;
+ outline-offset: -1px;
+ outline: 1px dotted $dark-gray-500;
}
// Menu items.
diff --git a/packages/edit-post/src/components/sidebar/settings-header/style.scss b/packages/edit-post/src/components/sidebar/settings-header/style.scss
index 3fcfe94481b4f..457f7e0361b21 100644
--- a/packages/edit-post/src/components/sidebar/settings-header/style.scss
+++ b/packages/edit-post/src/components/sidebar/settings-header/style.scss
@@ -18,14 +18,14 @@
.edit-post-sidebar__panel-tab {
background: transparent;
border: none;
- border-radius: 0;
+ box-shadow: none;
cursor: pointer;
- height: $grid-size * 6;
padding: 3px 15px; // Use padding to offset the is-active border, this benefits Windows High Contrast mode
margin-left: 0;
font-weight: 400;
color: $dark-gray-900;
@include square-style__neutral;
+ transition: box-shadow 0.1s linear;
// This pseudo-element "duplicates" the tab label and sets the text to bold.
// This ensures that the tab doesn't change width when selected.
@@ -41,9 +41,20 @@
}
&.is-active {
- padding-bottom: 0;
- border-bottom: 3px solid theme(primary);
+ box-shadow: inset 0 -3px theme(outlines);
font-weight: 600;
+ position: relative;
+
+ // This border appears in Windows High Contrast mode instead of the box-shadow.
+ &::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ bottom: 1px;
+ right: 0;
+ left: 0;
+ border-bottom: 3px solid transparent;
+ }
}
&:focus {
diff --git a/packages/edit-post/src/components/sidebar/style.scss b/packages/edit-post/src/components/sidebar/style.scss
index 823256093ffbb..cfc42eb75f7b3 100644
--- a/packages/edit-post/src/components/sidebar/style.scss
+++ b/packages/edit-post/src/components/sidebar/style.scss
@@ -163,18 +163,30 @@
.edit-post-sidebar__panel-tab {
background: transparent;
border: none;
- border-radius: 0;
+ box-shadow: none;
cursor: pointer;
height: 50px;
padding: 3px 15px; // Use padding to offset the is-active border, this benefits Windows High Contrast mode
margin-left: 0;
font-weight: 400;
@include square-style__neutral;
+ transition: box-shadow 0.1s linear;
&.is-active {
- padding-bottom: 0;
- border-bottom: 3px solid theme(primary);
+ box-shadow: inset 0 -3px theme(outlines);
font-weight: 600;
+ position: relative;
+
+ // This border appears in Windows High Contrast mode instead of the box-shadow.
+ &::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ bottom: 1px;
+ right: 0;
+ left: 0;
+ border-bottom: 3px solid transparent;
+ }
}
&:focus {
From eac0a6466b54e79bed7f32293aa50aeb00cdd605 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Tue, 26 Feb 2019 16:46:36 -0500
Subject: [PATCH 030/169] Plugin: Remove vendor script registration (#13573)
---
lib/client-assets.php | 92 ++-----------------------------------------
1 file changed, 4 insertions(+), 88 deletions(-)
diff --git a/lib/client-assets.php b/lib/client-assets.php
index 72d3522075a01..ac84f94a19025 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -139,21 +139,6 @@ function gutenberg_register_scripts_and_styles() {
global $wp_scripts;
gutenberg_register_vendor_scripts();
-
- wp_add_inline_script(
- 'wp-polyfill',
- wp_get_script_polyfill(
- $wp_scripts,
- array(
- '\'fetch\' in window' => 'wp-polyfill-fetch',
- 'document.contains' => 'wp-polyfill-node-contains',
- 'window.FormData && window.FormData.prototype.keys' => 'wp-polyfill-formdata',
- 'Element.prototype.matches && Element.prototype.closest' => 'wp-polyfill-element-closest',
- ),
- 'after'
- )
- );
-
gutenberg_register_packages_scripts();
// Inline scripts.
@@ -246,33 +231,6 @@ function gutenberg_register_scripts_and_styles() {
),
'after'
);
- wp_add_inline_script(
- 'moment',
- sprintf(
- "moment.locale( '%s', %s );",
- get_user_locale(),
- wp_json_encode(
- array(
- 'months' => array_values( $wp_locale->month ),
- 'monthsShort' => array_values( $wp_locale->month_abbrev ),
- 'weekdays' => array_values( $wp_locale->weekday ),
- 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
- 'week' => array(
- 'dow' => (int) get_option( 'start_of_week', 0 ),
- ),
- 'longDateFormat' => array(
- 'LT' => get_option( 'time_format', __( 'g:i a', 'default' ) ),
- 'LTS' => null,
- 'L' => null,
- 'LL' => get_option( 'date_format', __( 'F j, Y', 'default' ) ),
- 'LLL' => __( 'F j, Y g:i a', 'default' ),
- 'LLLL' => null,
- ),
- )
- )
- ),
- 'after'
- );
// Loading the old editor and its config to ensure the classic block works as expected.
wp_add_inline_script(
'editor',
@@ -534,52 +492,10 @@ function gutenberg_preload_api_request( $memo, $path ) {
* @since 0.1.0
*/
function gutenberg_register_vendor_scripts() {
- $suffix = SCRIPT_DEBUG ? '' : '.min';
-
- // Vendor Scripts.
- $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix;
-
- gutenberg_register_vendor_script(
- 'react',
- 'https://unpkg.com/react@16.6.3/umd/react' . $react_suffix . '.js',
- array( 'wp-polyfill' )
- );
- gutenberg_register_vendor_script(
- 'react-dom',
- 'https://unpkg.com/react-dom@16.6.3/umd/react-dom' . $react_suffix . '.js',
- array( 'react' )
- );
- $moment_script = SCRIPT_DEBUG ? 'moment.js' : 'min/moment.min.js';
- gutenberg_register_vendor_script(
- 'moment',
- 'https://unpkg.com/moment@2.22.1/' . $moment_script,
- array()
- );
- gutenberg_register_vendor_script(
- 'lodash',
- 'https://unpkg.com/lodash@4.17.11/lodash' . $suffix . '.js'
- );
- wp_add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
- gutenberg_register_vendor_script(
- 'wp-polyfill-fetch',
- 'https://unpkg.com/whatwg-fetch@3.0.0/dist/fetch.umd.js'
- );
- gutenberg_register_vendor_script(
- 'wp-polyfill-formdata',
- 'https://unpkg.com/formdata-polyfill@3.0.9/formdata.min.js'
- );
- gutenberg_register_vendor_script(
- 'wp-polyfill-node-contains',
- 'https://unpkg.com/polyfill-library@3.26.0-0/polyfills/Node/prototype/contains/polyfill.js'
- );
- gutenberg_register_vendor_script(
- 'wp-polyfill-element-closest',
- 'https://unpkg.com/element-closest@2.0.2/element-closest.js'
- );
- gutenberg_register_vendor_script(
- 'wp-polyfill',
- 'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill' . $suffix . '.js'
- );
+ /*
+ * This function is kept as an empty stub, in case Gutenberg should need to
+ * explicitly provide a version newer than that provided by core.
+ */
}
/**
From fd35a1b7045d01723de6b884123b4d74ef41084d Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Tue, 26 Feb 2019 22:13:04 +0000
Subject: [PATCH 031/169] chore: Fix: FormToggle docs documents props the
component does not uses (#14099)
---
packages/components/src/form-toggle/README.md | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/packages/components/src/form-toggle/README.md b/packages/components/src/form-toggle/README.md
index e2bfcefb07196..f247a2a12d1a6 100644
--- a/packages/components/src/form-toggle/README.md
+++ b/packages/components/src/form-toggle/README.md
@@ -71,20 +71,6 @@ const MyFormToggle = withState( {
The component accepts the following props:
-#### label
-
-If this property is added, a label will be generated using label property as the content.
-
-- Type: `String`
-- Required: No
-
-#### help
-
-If this property is added, a help text will be generated using help property as the content.
-
-- Type: `String` | `Function`
-- Required: No
-
#### checked
If checked is true the toggle will be checked. If checked is false the toggle will be unchecked.
From 1010f0da5599073f27860622a68aa9bc69426d33 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?=
Date: Wed, 27 Feb 2019 06:56:48 +0100
Subject: [PATCH 032/169] Babel Plugin Import JSX Pragma: Remove import visitor
(#14106)
Props to @aduth for coding this optimization.
---
.../CHANGELOG.md | 5 +++-
.../babel-plugin-import-jsx-pragma/index.js | 28 +------------------
2 files changed, 5 insertions(+), 28 deletions(-)
diff --git a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
index 32ad201bf1884..7a7c426e6ead9 100644
--- a/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
+++ b/packages/babel-plugin-import-jsx-pragma/CHANGELOG.md
@@ -2,9 +2,12 @@
### Breaking Change
-- Plugin skips now adding import JSX pragma when the scope variable is defined for all JSX elements ([#13809](https://github.com/WordPress/gutenberg/pull/13809)).
- Stop using Babel transpilation internally and set node 8 as a minimal version required ([#13540](https://github.com/WordPress/gutenberg/pull/13540)).
+### Enhancement
+
+- Plugin skips now adding import JSX pragma when the scope variable is defined for all JSX elements ([#13809](https://github.com/WordPress/gutenberg/pull/13809)).
+
## 1.1.0 (2018-09-05)
### New Feature
diff --git a/packages/babel-plugin-import-jsx-pragma/index.js b/packages/babel-plugin-import-jsx-pragma/index.js
index d431b6823aea3..7953640c484ab 100644
--- a/packages/babel-plugin-import-jsx-pragma/index.js
+++ b/packages/babel-plugin-import-jsx-pragma/index.js
@@ -40,42 +40,16 @@ module.exports = function( babel ) {
return {
visitor: {
JSXElement( path, state ) {
- state.hasJSX = true;
if ( state.hasUndeclaredScopeVariable ) {
return;
}
const { scopeVariable } = getOptions( state );
-
state.hasUndeclaredScopeVariable = ! path.scope.hasBinding( scopeVariable );
},
- ImportDeclaration( path, state ) {
- if ( state.hasImportedScopeVariable ) {
- return;
- }
-
- const { scopeVariable, isDefault } = getOptions( state );
-
- // Test that at least one import specifier exists matching the
- // scope variable name. The module source is not verified since
- // we must avoid introducing a conflicting import name, even if
- // the scope variable is referenced from a different source.
- state.hasImportedScopeVariable = path.node.specifiers.some( ( specifier ) => {
- switch ( specifier.type ) {
- case 'ImportSpecifier':
- return (
- ! isDefault &&
- specifier.imported.name === scopeVariable
- );
-
- case 'ImportDefaultSpecifier':
- return isDefault;
- }
- } );
- },
Program: {
exit( path, state ) {
- if ( ! state.hasJSX || state.hasImportedScopeVariable || ! state.hasUndeclaredScopeVariable ) {
+ if ( ! state.hasUndeclaredScopeVariable ) {
return;
}
From 1ed999561b4b6c6d0f0a520e4ff0e302af14d09e Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Wed, 27 Feb 2019 09:45:11 +0000
Subject: [PATCH 033/169] Add: Block specific toolbar button sample to the
format api tutorial (#14113)
## Description
This PR updates the format API tutorial (toolbar button section) to include a sample of a button that only renders on a certain block.
Answers a question posted on https://github.com/WordPress/gutenberg/issues/14104.
Closes: https://github.com/WordPress/gutenberg/issues/14104
## How has this been tested?
I pasted the sample code on the browser console and verified it works as expected.
---
.../tutorials/format-api/2-toolbar-button.md | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/docs/designers-developers/developers/tutorials/format-api/2-toolbar-button.md b/docs/designers-developers/developers/tutorials/format-api/2-toolbar-button.md
index 8aea061c5ef16..c6e72011f1782 100644
--- a/docs/designers-developers/developers/tutorials/format-api/2-toolbar-button.md
+++ b/docs/designers-developers/developers/tutorials/format-api/2-toolbar-button.md
@@ -35,3 +35,54 @@ Let's check that everything is working as expected. Reload the post/page and sel
![Toolbar with custom button](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/assets/toolbar-with-custom-button.png)
You may also want to check that upon clicking the button the `toggle format` message is shown in your browser's console.
+
+## Show the button only for specific blocks
+
+By default, the button is rendered on every rich text toolbar (image captions, buttons, paragraphs, etc).
+It is possible to render the button only on blocks of a certain type by using `wp.data.withSelect` together with `wp.compose.ifCondition`.
+The following sample code renders the previously shown button only on paragraph blocks:
+
+```js
+( function( wp ) {
+ var withSelect = wp.data.withSelect;
+ var ifCondition = wp.compose.ifCondition;
+ var compose = wp.compose.compose;
+ var MyCustomButton = function( props ) {
+ return wp.element.createElement(
+ wp.editor.RichTextToolbarButton, {
+ icon: 'editor-code',
+ title: 'Sample output',
+ onClick: function() {
+ console.log( 'toggle format' );
+ },
+ }
+ );
+ }
+ var ConditionalButton = compose(
+ withSelect( function( select ) {
+ return {
+ selectedBlock: select( 'core/editor' ).getSelectedBlock()
+ }
+ } ),
+ ifCondition( function( props ) {
+ return (
+ props.selectedBlock &&
+ props.selectedBlock.name === 'core/paragraph'
+ );
+ } )
+ )( MyCustomButton );
+
+ wp.richText.registerFormatType(
+ 'my-custom-format/sample-output', {
+ title: 'Sample output',
+ tagName: 'samp',
+ className: null,
+ edit: ConditionalButton,
+ }
+ );
+} )( window.wp );
+```
+
+Don't forget adding `wp-compose` and `wp-data` to the dependencies array in the PHP script.
+
+More advanced conditions can be used, e.g., only render the button depending on specific attributes of the block.
From 05570ff1bf0907ec3a801d78d1578a32ca65565b Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Wed, 27 Feb 2019 13:19:17 +0100
Subject: [PATCH 034/169] Use the editor settings to pass a mediaUpload handler
(#14115)
* Use the editor settings to pass a mediaUpload handler
* Update media block snapshots
---
.../audio/test/__snapshots__/index.js.snap | 32 ----------------
.../cover/test/__snapshots__/index.js.snap | 35 +----------------
.../gallery/test/__snapshots__/index.js.snap | 36 +-----------------
.../video/test/__snapshots__/index.js.snap | 32 ----------------
.../src/components/media-placeholder/index.js | 38 +++++++++++--------
.../editor/src/components/provider/index.js | 3 ++
6 files changed, 27 insertions(+), 149 deletions(-)
diff --git a/packages/block-library/src/audio/test/__snapshots__/index.js.snap b/packages/block-library/src/audio/test/__snapshots__/index.js.snap
index 6ebf5b5e4f09c..09f551226b702 100644
--- a/packages/block-library/src/audio/test/__snapshots__/index.js.snap
+++ b/packages/block-library/src/audio/test/__snapshots__/index.js.snap
@@ -38,38 +38,6 @@ exports[`core/audio block edit matches snapshot 1`] = `
-
-
diff --git a/packages/block-library/src/cover/test/__snapshots__/index.js.snap b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
index 44e8409be5d7b..36fb23ceb5916 100644
--- a/packages/block-library/src/cover/test/__snapshots__/index.js.snap
+++ b/packages/block-library/src/cover/test/__snapshots__/index.js.snap
@@ -37,39 +37,6 @@ exports[`core/cover block edit matches snapshot 1`] = `
+ />
`;
diff --git a/packages/block-library/src/gallery/test/__snapshots__/index.js.snap b/packages/block-library/src/gallery/test/__snapshots__/index.js.snap
index 9fd3dff017b9d..6e88dd4a4f949 100644
--- a/packages/block-library/src/gallery/test/__snapshots__/index.js.snap
+++ b/packages/block-library/src/gallery/test/__snapshots__/index.js.snap
@@ -39,40 +39,6 @@ exports[`core/gallery block edit matches snapshot 1`] = `
+ />
`;
diff --git a/packages/block-library/src/video/test/__snapshots__/index.js.snap b/packages/block-library/src/video/test/__snapshots__/index.js.snap
index d728e5e6b6ce0..88942a51fb5c1 100644
--- a/packages/block-library/src/video/test/__snapshots__/index.js.snap
+++ b/packages/block-library/src/video/test/__snapshots__/index.js.snap
@@ -38,38 +38,6 @@ exports[`core/video block edit matches snapshot 1`] = `
-
+
%
-
+
Date: Thu, 28 Feb 2019 08:24:19 -0500
Subject: [PATCH 041/169] Testing: Remove unnecessary Enzyme React 16
workarounds (#14156)
* Testing: Bump `enzyme-adapter-react-16` to 1.10.0
* Testing: Avoid unforwarded Button mock
No longer necessary with native support for forwardRef in Enzyme
* Testing: Un-skip BlockControls snapshot test
---
package-lock.json | 2 +-
.../components/src/button/__mocks__/index.js | 17 -----
.../test/__snapshots__/index.js.snap | 8 +--
.../src/color-palette/test/index.js | 2 -
.../components/src/icon-button/test/index.js | 10 ++-
.../test/__snapshots__/index.js.snap | 8 +--
.../components/src/menu-item/test/index.js | 2 -
packages/components/src/panel/test/body.js | 2 -
.../test/__snapshots__/index.js.snap | 4 +-
.../components/header/more-menu/test/index.js | 2 -
.../plugin-post-publish-panel/test/index.js | 2 -
.../plugin-pre-publish-panel/test/index.js | 2 -
.../test/__snapshots__/index.js.snap | 69 +++++++++++--------
.../components/block-controls/test/index.js | 17 +++--
.../test/__snapshots__/index.js.snap | 8 +--
.../post-preview-button/test/index.js | 2 -
.../post-publish-button/test/index.js | 4 +-
packages/jest-preset-default/CHANGELOG.md | 1 +
packages/jest-preset-default/package.json | 2 +-
.../dot-tip/test/__snapshots__/index.js.snap | 4 +-
.../nux/src/components/dot-tip/test/index.js | 4 +-
21 files changed, 78 insertions(+), 94 deletions(-)
delete mode 100644 packages/components/src/button/__mocks__/index.js
diff --git a/package-lock.json b/package-lock.json
index a86ed000f301b..b9bda6a34c73e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2895,7 +2895,7 @@
"@wordpress/jest-console": "file:packages/jest-console",
"babel-jest": "^24.1.0",
"enzyme": "^3.9.0",
- "enzyme-adapter-react-16": "^1.9.1",
+ "enzyme-adapter-react-16": "^1.10.0",
"enzyme-to-json": "^3.3.5"
}
},
diff --git a/packages/components/src/button/__mocks__/index.js b/packages/components/src/button/__mocks__/index.js
deleted file mode 100644
index d43b7380b09ec..0000000000000
--- a/packages/components/src/button/__mocks__/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// [TEMPORARY]: Button uses React.forwardRef, added in react@16.3.0 but not yet
-// supported by Enzyme as of enzyme-adapter-react-16@1.1.1 . This mock unwraps
-// the ref forwarding, so any tests relying on this behavior will fail.
-//
-// See: https://github.com/airbnb/enzyme/issues/1604
-// See: https://github.com/airbnb/enzyme/pull/1592/files
-
-const { Component } = require( 'react' );
-const { Button: RawButton } = require.requireActual( '../index' );
-
-class Button extends Component {
- render() {
- return RawButton( this.props );
- }
-}
-
-export default Button;
diff --git a/packages/components/src/color-palette/test/__snapshots__/index.js.snap b/packages/components/src/color-palette/test/__snapshots__/index.js.snap
index 3a49ace1959ad..e0ad2ef51ff3d 100644
--- a/packages/components/src/color-palette/test/__snapshots__/index.js.snap
+++ b/packages/components/src/color-palette/test/__snapshots__/index.js.snap
@@ -180,7 +180,7 @@ exports[`ColorPalette should allow disabling custom color picker 1`] = `
-
Clear
-
+
`;
@@ -272,7 +272,7 @@ exports[`ColorPalette should render a dynamic toolbar of colors 1`] = `
renderContent={[Function]}
renderToggle={[Function]}
/>
-
Clear
-
+
`;
diff --git a/packages/components/src/color-palette/test/index.js b/packages/components/src/color-palette/test/index.js
index 8983537ed7dc1..a515b9b769628 100644
--- a/packages/components/src/color-palette/test/index.js
+++ b/packages/components/src/color-palette/test/index.js
@@ -8,8 +8,6 @@ import { shallow } from 'enzyme';
*/
import ColorPalette from '../';
-jest.mock( '../../button' );
-
describe( 'ColorPalette', () => {
const colors = [ { name: 'red', color: '#f00' }, { name: 'white', color: '#fff' }, { name: 'blue', color: '#00f' } ];
const currentColor = '#f00';
diff --git a/packages/components/src/icon-button/test/index.js b/packages/components/src/icon-button/test/index.js
index d5b67eec7930f..133224941c1b5 100644
--- a/packages/components/src/icon-button/test/index.js
+++ b/packages/components/src/icon-button/test/index.js
@@ -8,8 +8,6 @@ import { shallow } from 'enzyme';
*/
import IconButton from '../';
-jest.mock( '../../button' );
-
describe( 'IconButton', () => {
describe( 'basic rendering', () => {
it( 'should render an top level element with only a class property', () => {
@@ -30,7 +28,7 @@ describe( 'IconButton', () => {
it( 'should add an aria-label when the label property is used', () => {
const iconButton = shallow( WordPress );
- expect( iconButton.name() ).toBe( 'Button' );
+ expect( iconButton.name() ).toBe( 'ForwardRef(Button)' );
expect( iconButton.prop( 'aria-label' ) ).toBe( 'WordPress' );
} );
@@ -38,7 +36,7 @@ describe( 'IconButton', () => {
const iconButton = shallow( );
expect( iconButton.name() ).toBe( 'Tooltip' );
expect( iconButton.prop( 'text' ) ).toBe( 'WordPress' );
- expect( iconButton.find( 'Button' ).prop( 'aria-label' ) ).toBe( 'WordPress' );
+ expect( iconButton.find( 'ForwardRef(Button)' ).prop( 'aria-label' ) ).toBe( 'WordPress' );
} );
it( 'should support explicit aria-label override', () => {
@@ -60,12 +58,12 @@ describe( 'IconButton', () => {
const iconButton = shallow( );
expect( iconButton.name() ).toBe( 'Tooltip' );
expect( iconButton.prop( 'text' ) ).toBe( 'Custom' );
- expect( iconButton.find( 'Button' ).prop( 'aria-label' ) ).toBe( 'WordPress' );
+ expect( iconButton.find( 'ForwardRef(Button)' ).prop( 'aria-label' ) ).toBe( 'WordPress' );
} );
it( 'should allow tooltip disable', () => {
const iconButton = shallow( );
- expect( iconButton.name() ).toBe( 'Button' );
+ expect( iconButton.name() ).toBe( 'ForwardRef(Button)' );
expect( iconButton.prop( 'aria-label' ) ).toBe( 'WordPress' );
} );
diff --git a/packages/components/src/menu-item/test/__snapshots__/index.js.snap b/packages/components/src/menu-item/test/__snapshots__/index.js.snap
index 9ec9378f631a1..caa467a67a66b 100644
--- a/packages/components/src/menu-item/test/__snapshots__/index.js.snap
+++ b/packages/components/src/menu-item/test/__snapshots__/index.js.snap
@@ -17,7 +17,7 @@ exports[`MenuItem should match snapshot when all props provided 1`] = `
`;
exports[`MenuItem should match snapshot when info is provided 1`] = `
-
-
+
`;
exports[`MenuItem should match snapshot when isSelected and role are optionally provided 1`] = `
@@ -55,7 +55,7 @@ exports[`MenuItem should match snapshot when isSelected and role are optionally
`;
exports[`MenuItem should match snapshot when only label provided 1`] = `
-
@@ -63,5 +63,5 @@ exports[`MenuItem should match snapshot when only label provided 1`] = `
-
+
`;
diff --git a/packages/components/src/menu-item/test/index.js b/packages/components/src/menu-item/test/index.js
index c5508e55629ac..f521add7452c8 100644
--- a/packages/components/src/menu-item/test/index.js
+++ b/packages/components/src/menu-item/test/index.js
@@ -9,8 +9,6 @@ import { noop } from 'lodash';
*/
import { MenuItem } from '../';
-jest.mock( '../../button' );
-
describe( 'MenuItem', () => {
it( 'should match snapshot when only label provided', () => {
const wrapper = shallow(
diff --git a/packages/components/src/panel/test/body.js b/packages/components/src/panel/test/body.js
index cda8c66bbf6d5..78d4774afccd8 100644
--- a/packages/components/src/panel/test/body.js
+++ b/packages/components/src/panel/test/body.js
@@ -8,8 +8,6 @@ import { shallow, mount } from 'enzyme';
*/
import { PanelBody } from '../body';
-jest.mock( '../../button' );
-
describe( 'PanelBody', () => {
describe( 'basic rendering', () => {
it( 'should render an empty div with the matching className', () => {
diff --git a/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap
index 4049a747b3fe0..9e8a474f6f921 100644
--- a/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap
+++ b/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap
@@ -23,7 +23,7 @@ exports[`MoreMenu should match snapshot 1`] = `
position="bottom"
text="Show more tools & options"
>
-
-
+
diff --git a/packages/edit-post/src/components/header/more-menu/test/index.js b/packages/edit-post/src/components/header/more-menu/test/index.js
index 92daff0997151..83f9de19ba706 100644
--- a/packages/edit-post/src/components/header/more-menu/test/index.js
+++ b/packages/edit-post/src/components/header/more-menu/test/index.js
@@ -8,8 +8,6 @@ import { mount } from 'enzyme';
*/
import MoreMenu from '../index';
-jest.mock( '../../../../../../components/src/button' );
-
describe( 'MoreMenu', () => {
it( 'should match snapshot', () => {
const wrapper = mount(
diff --git a/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/test/index.js b/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/test/index.js
index 66b159bb25533..f4b5ca77eca16 100644
--- a/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/test/index.js
+++ b/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/test/index.js
@@ -9,8 +9,6 @@ import { render } from '@wordpress/element';
*/
import PluginPostPublishPanel from '../';
-jest.mock( '../../../../../../components/src/button' );
-
describe( 'PluginPostPublishPanel', () => {
test( 'renders fill properly', () => {
const div = document.createElement( 'div' );
diff --git a/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/test/index.js b/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/test/index.js
index 1383341bfe705..9c011e1cbc2a0 100644
--- a/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/test/index.js
+++ b/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/test/index.js
@@ -9,8 +9,6 @@ import { render } from '@wordpress/element';
*/
import PluginPrePublishPanel from '../';
-jest.mock( '../../../../../../components/src/button' );
-
describe( 'PluginPrePublishPanel', () => {
test( 'renders fill properly', () => {
const div = document.createElement( 'div' );
diff --git a/packages/editor/src/components/block-controls/test/__snapshots__/index.js.snap b/packages/editor/src/components/block-controls/test/__snapshots__/index.js.snap
index f1cf3c119e9ad..681c33a42d997 100644
--- a/packages/editor/src/components/block-controls/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/block-controls/test/__snapshots__/index.js.snap
@@ -1,32 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`BlockControls Should render a dynamic toolbar of controls 1`] = `
-
-
-
- Child
-
-
+ }
+>
+
+
+
+ Child
+
+
+
+
`;
diff --git a/packages/editor/src/components/block-controls/test/index.js b/packages/editor/src/components/block-controls/test/index.js
index 1eee2fbf5af2a..f970dc030cadb 100644
--- a/packages/editor/src/components/block-controls/test/index.js
+++ b/packages/editor/src/components/block-controls/test/index.js
@@ -6,7 +6,8 @@ import { shallow } from 'enzyme';
/**
* Internal dependencies
*/
-import { BlockControls } from '../';
+import BlockControls from '../';
+import BlockEdit from '../../block-edit';
describe( 'BlockControls', () => {
const controls = [
@@ -27,9 +28,15 @@ describe( 'BlockControls', () => {
},
];
- // Skipped temporarily until Enzyme publishes new version that works with React 16.3.0 APIs.
- // eslint-disable-next-line jest/no-disabled-tests
- test.skip( 'Should render a dynamic toolbar of controls', () => {
- expect( shallow( Child
} /> ) ).toMatchSnapshot();
+ it( 'should render a dynamic toolbar of controls', () => {
+ const wrapper = shallow(
+
+
+ Child
+
+
+ );
+
+ expect( wrapper ).toMatchSnapshot();
} );
} );
diff --git a/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap b/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap
index cfbde2342e780..fc928b5e12cb9 100644
--- a/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PostPreviewButton render() should render currentPostLink otherwise 1`] = `
-
Click “Preview” to load a preview of this page, so you can make sure you’re happy with your blocks.
-
+
`;
exports[`PostPreviewButton render() should render previewLink if provided 1`] = `
-
Click “Preview” to load a preview of this page, so you can make sure you’re happy with your blocks.
-
+
`;
diff --git a/packages/editor/src/components/post-preview-button/test/index.js b/packages/editor/src/components/post-preview-button/test/index.js
index 6920f27fc809c..7bd9856de05c7 100644
--- a/packages/editor/src/components/post-preview-button/test/index.js
+++ b/packages/editor/src/components/post-preview-button/test/index.js
@@ -8,8 +8,6 @@ import { shallow } from 'enzyme';
*/
import { PostPreviewButton } from '../';
-jest.mock( '../../../../../components/src/button' );
-
describe( 'PostPreviewButton', () => {
describe( 'setPreviewWindowLink()', () => {
it( 'should do nothing if there is no preview window', () => {
diff --git a/packages/editor/src/components/post-publish-button/test/index.js b/packages/editor/src/components/post-publish-button/test/index.js
index 30156438cc14f..be407f86e14cd 100644
--- a/packages/editor/src/components/post-publish-button/test/index.js
+++ b/packages/editor/src/components/post-publish-button/test/index.js
@@ -8,8 +8,6 @@ import { shallow } from 'enzyme';
*/
import { PostPublishButton } from '../';
-jest.mock( '../../../../../components/src/button' );
-
describe( 'PostPublishButton', () => {
describe( 'aria-disabled', () => {
it( 'should be true if post is currently saving', () => {
@@ -196,6 +194,6 @@ describe( 'PostPublishButton', () => {
/>
);
- expect( wrapper.find( 'Button' ).prop( 'isBusy' ) ).toBe( true );
+ expect( wrapper.find( 'ForwardRef(Button)' ).prop( 'isBusy' ) ).toBe( true );
} );
} );
diff --git a/packages/jest-preset-default/CHANGELOG.md b/packages/jest-preset-default/CHANGELOG.md
index 7ff9fd2ced551..04eb1446a749d 100644
--- a/packages/jest-preset-default/CHANGELOG.md
+++ b/packages/jest-preset-default/CHANGELOG.md
@@ -8,6 +8,7 @@
### Internal
- The bundled `enzyme` dependency has been updated from requiring `^3.7.0` to requiring `^3.9.0` ([#13922](https://github.com/WordPress/gutenberg/pull/13922)).
+- The bundled `enzyme-adapter-react-16` dependency has been updated from requiring `^1.6.0` to requiring `^1.10.0` ([#13922](https://github.com/WordPress/gutenberg/pull/13922)).
## 3.0.3 (2018-11-20)
diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json
index bc73370bb4706..e8bbd45dd0b8b 100644
--- a/packages/jest-preset-default/package.json
+++ b/packages/jest-preset-default/package.json
@@ -33,7 +33,7 @@
"@wordpress/jest-console": "file:../jest-console",
"babel-jest": "^24.1.0",
"enzyme": "^3.9.0",
- "enzyme-adapter-react-16": "^1.9.1",
+ "enzyme-adapter-react-16": "^1.10.0",
"enzyme-to-json": "^3.3.5"
},
"peerDependencies": {
diff --git a/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap b/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap
index d9639628174e7..155eac26c893a 100644
--- a/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap
+++ b/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap
@@ -15,11 +15,11 @@ exports[`DotTip should render correctly 1`] = `
It looks like you’re writing a letter. Would you like help?
-
Got it
-
+
{
it( 'should not render anything if invisible', () => {
const wrapper = shallow(
@@ -37,7 +35,7 @@ describe( 'DotTip', () => {
It looks like you’re writing a letter. Would you like help?
);
- wrapper.find( 'Button[children="Got it"]' ).first().simulate( 'click' );
+ wrapper.find( 'ForwardRef(Button)[children="Got it"]' ).first().simulate( 'click' );
expect( onDismiss ).toHaveBeenCalled();
} );
From f7b002d284ff1a08bc257c5dc41303a7f532789f Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Thu, 28 Feb 2019 14:11:39 -0500
Subject: [PATCH 042/169] Plugin: Preserve inline scripts in Gutenberg override
(#13581)
* Plugin: Preserve inline scripts in Gutenberg override
* Plugin: Restore storageKey assignment for persistence migration
---
lib/client-assets.php | 209 ++++++-------------------
phpunit/class-override-script-test.php | 66 ++++++++
2 files changed, 113 insertions(+), 162 deletions(-)
create mode 100644 phpunit/class-override-script-test.php
diff --git a/lib/client-assets.php b/lib/client-assets.php
index 834a08a2d83a9..dc9fff22a8386 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -36,7 +36,9 @@ function gutenberg_url( $path ) {
/**
* Registers a script according to `wp_register_script`. Honors this request by
- * deregistering any script by the same handler before registration.
+ * reassigning internal dependency properties of any script handle already
+ * registered by that name. It does not deregister the original script, to
+ * avoid losing inline scripts which may have been attached.
*
* @since 4.1.0
*
@@ -51,8 +53,36 @@ function gutenberg_url( $path ) {
* Default 'false'.
*/
function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
- wp_deregister_script( $handle );
- wp_register_script( $handle, $src, $deps, $ver, $in_footer );
+ global $wp_scripts;
+
+ $script = $wp_scripts->query( $handle, 'registered' );
+ if ( $script ) {
+ /*
+ * In many ways, this is a reimplementation of `wp_register_script` but
+ * bypassing consideration of whether a script by the given handle had
+ * already been registered.
+ */
+
+ // See: `_WP_Dependency::__construct` .
+ $script->src = $src;
+ $script->deps = $deps;
+ $script->ver = $ver;
+
+ /*
+ * The script's `group` designation is an indication of whether it is
+ * to be printed in the header or footer. The behavior here defers to
+ * the arguments as passed. Specifically, group data is not assigned
+ * for a script unless it is designated to be printed in the footer.
+ */
+
+ // See: `wp_register_script` .
+ unset( $script->extra['group'] );
+ if ( $in_footer ) {
+ $script->add_data( 'group', 1 );
+ }
+ } else {
+ wp_register_script( $handle, $src, $deps, $ver, $in_footer );
+ }
}
/**
@@ -112,7 +142,15 @@ function gutenberg_register_scripts_and_styles() {
gutenberg_register_vendor_scripts();
gutenberg_register_packages_scripts();
- // Inline scripts.
+ // Add nonce middleware which accounts for the absence of the heartbeat
+ // listener. This relies on API Fetch implementation running middlewares in
+ // order of last added, and that the original nonce middleware would defer
+ // to an X-WP-Nonce header already being present. This inline script should
+ // be removed once the following Core ticket is resolved in assigning the
+ // nonce received from heartbeat to the created middleware.
+ //
+ // See: https://core.trac.wordpress.org/ticket/46107 .
+ // See: https://github.com/WordPress/gutenberg/pull/13451 .
global $wp_scripts;
if ( isset( $wp_scripts->registered['wp-api-fetch'] ) ) {
$wp_scripts->registered['wp-api-fetch']->deps[] = 'wp-hooks';
@@ -135,21 +173,16 @@ function gutenberg_register_scripts_and_styles() {
' }',
' }',
' )',
- '} )()',
+ '} )();',
)
),
( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' )
),
'after'
);
- wp_add_inline_script(
- 'wp-api-fetch',
- sprintf(
- 'wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "%s" ) );',
- esc_url_raw( get_rest_url() )
- ),
- 'after'
- );
+
+ // TEMPORARY: Core does not (yet) provide persistence migration from the
+ // introduction of the block editor.
wp_add_inline_script(
'wp-data',
implode(
@@ -158,160 +191,11 @@ function gutenberg_register_scripts_and_styles() {
'( function() {',
' var userId = ' . get_current_user_ID() . ';',
' var storageKey = "WP_DATA_USER_" + userId;',
- ' wp.data',
- ' .use( wp.data.plugins.persistence, { storageKey: storageKey } )',
- ' .use( wp.data.plugins.controls );',
' wp.data.plugins.persistence.__unstableMigrate( { storageKey: storageKey } );',
'} )()',
)
)
);
- global $wp_locale;
- wp_add_inline_script(
- 'wp-date',
- sprintf(
- 'wp.date.setSettings( %s );',
- wp_json_encode(
- array(
- 'l10n' => array(
- 'locale' => get_user_locale(),
- 'months' => array_values( $wp_locale->month ),
- 'monthsShort' => array_values( $wp_locale->month_abbrev ),
- 'weekdays' => array_values( $wp_locale->weekday ),
- 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
- 'meridiem' => (object) $wp_locale->meridiem,
- 'relative' => array(
- /* translators: %s: duration */
- 'future' => __( '%s from now', 'default' ),
- /* translators: %s: duration */
- 'past' => __( '%s ago', 'default' ),
- ),
- ),
- 'formats' => array(
- 'time' => get_option( 'time_format', __( 'g:i a', 'default' ) ),
- 'date' => get_option( 'date_format', __( 'F j, Y', 'default' ) ),
- 'datetime' => __( 'F j, Y g:i a', 'default' ),
- 'datetimeAbbreviated' => __( 'M j, Y g:i a', 'default' ),
- ),
- 'timezone' => array(
- 'offset' => get_option( 'gmt_offset', 0 ),
- 'string' => get_option( 'timezone_string', 'UTC' ),
- ),
- )
- )
- ),
- 'after'
- );
- // Loading the old editor and its config to ensure the classic block works as expected.
- wp_add_inline_script(
- 'editor',
- 'window.wp.oldEditor = window.wp.editor;',
- 'after'
- );
-
- $tinymce_plugins = array(
- 'charmap',
- 'colorpicker',
- 'hr',
- 'lists',
- 'media',
- 'paste',
- 'tabfocus',
- 'textcolor',
- 'fullscreen',
- 'wordpress',
- 'wpautoresize',
- 'wpeditimage',
- 'wpemoji',
- 'wpgallery',
- 'wplink',
- 'wpdialogs',
- 'wptextpattern',
- 'wpview',
- );
- $tinymce_plugins = apply_filters( 'tiny_mce_plugins', $tinymce_plugins, 'classic-block' );
- $tinymce_plugins = array_unique( $tinymce_plugins );
-
- $toolbar1 = array(
- 'formatselect',
- 'bold',
- 'italic',
- 'bullist',
- 'numlist',
- 'blockquote',
- 'alignleft',
- 'aligncenter',
- 'alignright',
- 'link',
- 'unlink',
- 'wp_more',
- 'spellchecker',
- 'wp_add_media',
- 'kitchensink',
- );
- $toolbar1 = apply_filters( 'mce_buttons', $toolbar1, 'classic-block' );
-
- $toolbar2 = array(
- 'strikethrough',
- 'hr',
- 'forecolor',
- 'pastetext',
- 'removeformat',
- 'charmap',
- 'outdent',
- 'indent',
- 'undo',
- 'redo',
- 'wp_help',
- );
- $toolbar2 = apply_filters( 'mce_buttons_2', $toolbar2, 'classic-block' );
-
- $toolbar3 = apply_filters( 'mce_buttons_3', array(), 'classic-block' );
- $toolbar4 = apply_filters( 'mce_buttons_4', array(), 'classic-block' );
-
- $external_plugins = apply_filters( 'mce_external_plugins', array(), 'classic-block' );
-
- $tinymce_settings = array(
- 'plugins' => implode( ',', $tinymce_plugins ),
- 'toolbar1' => implode( ',', $toolbar1 ),
- 'toolbar2' => implode( ',', $toolbar2 ),
- 'toolbar3' => implode( ',', $toolbar3 ),
- 'toolbar4' => implode( ',', $toolbar4 ),
- 'external_plugins' => wp_json_encode( $external_plugins ),
- 'classic_block_editor' => true,
- );
- $tinymce_settings = apply_filters( 'tiny_mce_before_init', $tinymce_settings, 'classic-block' );
-
- // Do "by hand" translation from PHP array to js object.
- // Prevents breakage in some custom settings.
- $init_obj = '';
- foreach ( $tinymce_settings as $key => $value ) {
- if ( is_bool( $value ) ) {
- $val = $value ? 'true' : 'false';
- $init_obj .= $key . ':' . $val . ',';
- continue;
- } elseif ( ! empty( $value ) && is_string( $value ) && (
- ( '{' == $value{0} && '}' == $value{strlen( $value ) - 1} ) ||
- ( '[' == $value{0} && ']' == $value{strlen( $value ) - 1} ) ||
- preg_match( '/^\(?function ?\(/', $value ) ) ) {
-
- $init_obj .= $key . ':' . $value . ',';
- continue;
- }
- $init_obj .= $key . ':"' . $value . '",';
- }
-
- $init_obj = '{' . trim( $init_obj, ' ,' ) . '}';
-
- $script = 'window.wpEditorL10n = {
- tinymce: {
- baseURL: ' . wp_json_encode( includes_url( 'js/tinymce' ) ) . ',
- suffix: ' . ( SCRIPT_DEBUG ? '""' : '".min"' ) . ',
- settings: ' . $init_obj . ',
- }
- }';
-
- wp_add_inline_script( 'wp-block-library', $script, 'before' );
// Editor Styles.
// This empty stylesheet is defined to ensure backward compatibility.
@@ -1073,6 +957,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
'post' => $post->ID,
)
);
+ wp_tinymce_inline_scripts();
wp_enqueue_editor();
/**
diff --git a/phpunit/class-override-script-test.php b/phpunit/class-override-script-test.php
new file mode 100644
index 0000000000000..af9e5d73fff19
--- /dev/null
+++ b/phpunit/class-override-script-test.php
@@ -0,0 +1,66 @@
+query( 'gutenberg-dummy-script', 'registered' );
+ $this->assertEquals( 'https://example.com/updated', $script->src );
+ $this->assertEquals( array( 'updated-dependency' ), $script->deps );
+ $this->assertEquals( 'updated-version', $script->ver );
+ $this->assertEquals( 1, $script->extra['group'] );
+ }
+
+ /**
+ * Tests that new script registers normally if no handle by the name.
+ */
+ function test_registers_new_script() {
+ gutenberg_override_script(
+ 'gutenberg-second-dummy-script',
+ 'https://example.com/',
+ array( 'dependency' ),
+ 'version',
+ true
+ );
+
+ global $wp_scripts;
+ $script = $wp_scripts->query( 'gutenberg-second-dummy-script', 'registered' );
+ $this->assertEquals( 'https://example.com/', $script->src );
+ $this->assertEquals( array( 'dependency' ), $script->deps );
+ $this->assertEquals( 'version', $script->ver );
+ $this->assertEquals( 1, $script->extra['group'] );
+ }
+}
From c3cacd3d88014c913478d76e8bc24c849638f848 Mon Sep 17 00:00:00 2001
From: Darren Ethier
Date: Thu, 28 Feb 2019 14:26:11 -0500
Subject: [PATCH 043/169] Refactor to remove usage of post related effects in
packages/editor. (#13716)
This pull is the first step in moving away from the lingering usage of effects in various data stores among packages. This pull specifically deals with post related effects in the @wordpress/editor package (`core/editor` store).
---
.../developers/data/data-core-editor.md | 76 +-
packages/editor/CHANGELOG.md | 1 +
packages/editor/src/store/actions.js | 396 ++++++++-
packages/editor/src/store/constants.js | 12 +
packages/editor/src/store/controls.js | 103 ++-
packages/editor/src/store/effects.js | 20 -
packages/editor/src/store/effects/posts.js | 319 -------
packages/editor/src/store/index.js | 8 +-
packages/editor/src/store/selectors.js | 22 +-
packages/editor/src/store/test/actions.js | 775 +++++++++++++++++-
packages/editor/src/store/test/effects.js | 197 -----
packages/editor/src/store/test/selectors.js | 2 +-
.../editor/src/store/utils/notice-builder.js | 123 +++
.../src/store/utils/test/notice-builder.js | 182 ++++
14 files changed, 1588 insertions(+), 648 deletions(-)
delete mode 100644 packages/editor/src/store/effects/posts.js
create mode 100644 packages/editor/src/store/utils/notice-builder.js
create mode 100644 packages/editor/src/store/utils/test/notice-builder.js
diff --git a/docs/designers-developers/developers/data/data-core-editor.md b/docs/designers-developers/developers/data/data-core-editor.md
index 7e95832dc5bdb..3a22e11c90121 100644
--- a/docs/designers-developers/developers/data/data-core-editor.md
+++ b/docs/designers-developers/developers/data/data-core-editor.md
@@ -749,6 +749,43 @@ post has been received, by initialization or autosave.
* post: Autosave post object.
+### __experimentalRequestPostUpdateStart
+
+Optimistic action for dispatching that a post update request has started.
+
+*Parameters*
+
+ * options: null
+
+### __experimentalRequestPostUpdateSuccess
+
+Optimistic action for indicating that the request post update has completed
+successfully.
+
+*Parameters*
+
+ * data: The data for the action.
+ * data.previousPost: The previous post prior to update.
+ * data.post: The new post after update
+ * data.isRevision: Whether the post is a revision or not.
+ * data.options: Options passed through from the original
+ action dispatch.
+ * data.postType: The post type object.
+
+### __experimentalRequestPostUpdateFailure
+
+Optimistic action for indicating that the request post update has completed
+with a failure.
+
+*Parameters*
+
+ * data: The data for the action
+ * data.post: The post that failed updating.
+ * data.edits: The fields that were being updated.
+ * data.error: The error from the failed call.
+ * data.options: Options passed through from the original
+ action dispatch.
+
### updatePost
Returns an action object used in signalling that a patch of updates for the
@@ -760,7 +797,8 @@ latest version of the post have been received.
### setupEditorState
-Returns an action object used to setup the editor state when first opening an editor.
+Returns an action object used to setup the editor state when first opening
+an editor.
*Parameters*
@@ -775,18 +813,34 @@ been edited.
* edits: Post attributes to edit.
+### __experimentalOptimisticUpdatePost
+
+Returns action object produced by the updatePost creator augmented by
+an optimist option that signals optimistically applying updates.
+
+*Parameters*
+
+ * edits: Updated post fields.
+
### savePost
-Returns an action object to save the post.
+Action generator for saving the current post in the editor.
*Parameters*
- * options: Options for the save.
- * options.isAutosave: Perform an autosave if true.
+ * options: null
+
+### refreshPost
+
+Action generator for handling refreshing the current post.
+
+### trashPost
+
+Action generator for trashing the current post in the editor.
### autosave
-Returns an action object used in signalling that the post should autosave.
+Action generator used in signalling that the post should autosave.
*Parameters*
@@ -864,7 +918,8 @@ to be updated.
### __experimentalConvertBlockToStatic
-Returns an action object used to convert a reusable block into a static block.
+Returns an action object used to convert a reusable block into a static
+block.
*Parameters*
@@ -872,7 +927,8 @@ Returns an action object used to convert a reusable block into a static block.
### __experimentalConvertBlockToReusable
-Returns an action object used to convert a static block into a reusable block.
+Returns an action object used to convert a static block into a reusable
+block.
*Parameters*
@@ -880,11 +936,13 @@ Returns an action object used to convert a static block into a reusable block.
### enablePublishSidebar
-Returns an action object used in signalling that the user has enabled the publish sidebar.
+Returns an action object used in signalling that the user has enabled the
+publish sidebar.
### disablePublishSidebar
-Returns an action object used in signalling that the user has disabled the publish sidebar.
+Returns an action object used in signalling that the user has disabled the
+publish sidebar.
### lockPostSaving
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index 8b64a350c46ac..7c9eb8c623ed9 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -18,6 +18,7 @@
- Removed `jQuery` dependency.
- Removed `TinyMCE` dependency.
- RichText: improve format boundaries.
+- Refactor all post effects to action-generators using controls ([#13716](https://github.com/WordPress/gutenberg/pull/13716))
## 9.0.7 (2019-01-03)
diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js
index 0d508375c37c8..f1271ed3d96d1 100644
--- a/packages/editor/src/store/actions.js
+++ b/packages/editor/src/store/actions.js
@@ -1,12 +1,29 @@
/**
* External dependencies
*/
-import { castArray } from 'lodash';
+import { castArray, pick } from 'lodash';
+import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
/**
* Internal dependencies
*/
-import { dispatch } from './controls';
+import {
+ dispatch,
+ select,
+ resolveSelect,
+ apiFetch,
+} from './controls';
+import {
+ STORE_KEY,
+ POST_UPDATE_TRANSACTION_ID,
+ SAVE_POST_NOTICE_ID,
+ TRASH_POST_NOTICE_ID,
+} from './constants';
+import {
+ getNotificationArgumentsForSaveSuccess,
+ getNotificationArgumentsForSaveFail,
+ getNotificationArgumentsForTrashFail,
+} from './utils/notice-builder';
/**
* Returns an action object used in signalling that editor has initialized with
@@ -57,6 +74,87 @@ export function resetAutosave( post ) {
};
}
+/**
+ * Optimistic action for dispatching that a post update request has started.
+ *
+ * @param {Object} options
+ *
+ * @return {Object} An action object
+ */
+export function __experimentalRequestPostUpdateStart( options = {} ) {
+ return {
+ type: 'REQUEST_POST_UPDATE_START',
+ optimist: { type: BEGIN, id: POST_UPDATE_TRANSACTION_ID },
+ options,
+ };
+}
+
+/**
+ * Optimistic action for indicating that the request post update has completed
+ * successfully.
+ *
+ * @param {Object} data The data for the action.
+ * @param {Object} data.previousPost The previous post prior to update.
+ * @param {Object} data.post The new post after update
+ * @param {boolean} data.isRevision Whether the post is a revision or not.
+ * @param {Object} data.options Options passed through from the original
+ * action dispatch.
+ * @param {Object} data.postType The post type object.
+ *
+ * @return {Object} Action object.
+ */
+export function __experimentalRequestPostUpdateSuccess( {
+ previousPost,
+ post,
+ isRevision,
+ options,
+ postType,
+} ) {
+ return {
+ type: 'REQUEST_POST_UPDATE_SUCCESS',
+ previousPost,
+ post,
+ optimist: {
+ // Note: REVERT is not a failure case here. Rather, it
+ // is simply reversing the assumption that the updates
+ // were applied to the post proper, such that the post
+ // treated as having unsaved changes.
+ type: isRevision ? REVERT : COMMIT,
+ id: POST_UPDATE_TRANSACTION_ID,
+ },
+ options,
+ postType,
+ };
+}
+
+/**
+ * Optimistic action for indicating that the request post update has completed
+ * with a failure.
+ *
+ * @param {Object} data The data for the action
+ * @param {Object} data.post The post that failed updating.
+ * @param {Object} data.edits The fields that were being updated.
+ * @param {*} data.error The error from the failed call.
+ * @param {Object} data.options Options passed through from the original
+ * action dispatch.
+ * @return {Object} An action object
+ */
+export function __experimentalRequestPostUpdateFailure( {
+ post,
+ edits,
+ error,
+ options,
+} ) {
+ return {
+ type: 'REQUEST_POST_UPDATE_FAILURE',
+ optimist: { type: REVERT, id: POST_UPDATE_TRANSACTION_ID },
+ post,
+ edits,
+ error,
+ options,
+ };
+}
+
/**
* Returns an action object used in signalling that a patch of updates for the
* latest version of the post have been received.
@@ -73,7 +171,8 @@ export function updatePost( edits ) {
}
/**
- * Returns an action object used to setup the editor state when first opening an editor.
+ * Returns an action object used to setup the editor state when first opening
+ * an editor.
*
* @param {Object} post Post object.
*
@@ -102,43 +201,282 @@ export function editPost( edits ) {
}
/**
- * Returns an action object to save the post.
+ * Returns action object produced by the updatePost creator augmented by
+ * an optimist option that signals optimistically applying updates.
*
- * @param {Object} options Options for the save.
- * @param {boolean} options.isAutosave Perform an autosave if true.
+ * @param {Object} edits Updated post fields.
*
* @return {Object} Action object.
*/
-export function savePost( options = {} ) {
+export function __experimentalOptimisticUpdatePost( edits ) {
return {
- type: 'REQUEST_POST_UPDATE',
- options,
+ ...updatePost( edits ),
+ optimist: { id: POST_UPDATE_TRANSACTION_ID },
};
}
-export function refreshPost() {
- return {
- type: 'REFRESH_POST',
+/**
+ * Action generator for saving the current post in the editor.
+ *
+ * @param {Object} options
+ */
+export function* savePost( options = {} ) {
+ const isEditedPostSaveable = yield select(
+ STORE_KEY,
+ 'isEditedPostSaveable'
+ );
+ if ( ! isEditedPostSaveable ) {
+ return;
+ }
+ let edits = yield select(
+ STORE_KEY,
+ 'getPostEdits'
+ );
+ const isAutosave = !! options.isAutosave;
+
+ if ( isAutosave ) {
+ edits = pick( edits, [ 'title', 'content', 'excerpt' ] );
+ }
+
+ const isEditedPostNew = yield select(
+ STORE_KEY,
+ 'isEditedPostNew',
+ );
+
+ // New posts (with auto-draft status) must be explicitly assigned draft
+ // status if there is not already a status assigned in edits (publish).
+ // Otherwise, they are wrongly left as auto-draft. Status is not always
+ // respected for autosaves, so it cannot simply be included in the pick
+ // above. This behavior relies on an assumption that an auto-draft post
+ // would never be saved by anyone other than the owner of the post, per
+ // logic within autosaves REST controller to save status field only for
+ // draft/auto-draft by current user.
+ //
+ // See: https://core.trac.wordpress.org/ticket/43316#comment:88
+ // See: https://core.trac.wordpress.org/ticket/43316#comment:89
+ if ( isEditedPostNew ) {
+ edits = { status: 'draft', ...edits };
+ }
+
+ const post = yield select(
+ STORE_KEY,
+ 'getCurrentPost'
+ );
+
+ const editedPostContent = yield select(
+ STORE_KEY,
+ 'getEditedPostContent'
+ );
+
+ let toSend = {
+ ...edits,
+ content: editedPostContent,
+ id: post.id,
};
+
+ const currentPostType = yield select(
+ STORE_KEY,
+ 'getCurrentPostType'
+ );
+
+ const postType = yield resolveSelect(
+ 'core',
+ 'getPostType',
+ currentPostType
+ );
+
+ yield dispatch(
+ STORE_KEY,
+ '__experimentalRequestPostUpdateStart',
+ options,
+ );
+
+ // Optimistically apply updates under the assumption that the post
+ // will be updated. See below logic in success resolution for revert
+ // if the autosave is applied as a revision.
+ yield dispatch(
+ STORE_KEY,
+ '__experimentalOptimisticUpdatePost',
+ toSend
+ );
+
+ let path = `/wp/v2/${ postType.rest_base }/${ post.id }`;
+ let method = 'PUT';
+ if ( isAutosave ) {
+ const autoSavePost = yield select(
+ STORE_KEY,
+ 'getAutosave',
+ );
+ // Ensure autosaves contain all expected fields, using autosave or
+ // post values as fallback if not otherwise included in edits.
+ toSend = {
+ ...pick( post, [ 'title', 'content', 'excerpt' ] ),
+ ...autoSavePost,
+ ...toSend,
+ };
+ path += '/autosaves';
+ method = 'POST';
+ } else {
+ yield dispatch(
+ 'core/notices',
+ 'removeNotice',
+ SAVE_POST_NOTICE_ID,
+ );
+ yield dispatch(
+ 'core/notices',
+ 'removeNotice',
+ 'autosave-exists',
+ );
+ }
+
+ try {
+ const newPost = yield apiFetch( {
+ path,
+ method,
+ data: toSend,
+ } );
+ const resetAction = isAutosave ? 'resetAutosave' : 'resetPost';
+
+ yield dispatch( STORE_KEY, resetAction, newPost );
+
+ yield dispatch(
+ STORE_KEY,
+ '__experimentalRequestPostUpdateSuccess',
+ {
+ previousPost: post,
+ post: newPost,
+ options,
+ postType,
+ // An autosave may be processed by the server as a regular save
+ // when its update is requested by the author and the post was
+ // draft or auto-draft.
+ isRevision: newPost.id !== post.id,
+ }
+ );
+
+ const notifySuccessArgs = getNotificationArgumentsForSaveSuccess( {
+ previousPost: post,
+ post: newPost,
+ postType,
+ options,
+ } );
+ if ( notifySuccessArgs.length > 0 ) {
+ yield dispatch(
+ 'core/notices',
+ 'createSuccessNotice',
+ ...notifySuccessArgs
+ );
+ }
+ } catch ( error ) {
+ yield dispatch(
+ STORE_KEY,
+ '__experimentalRequestPostUpdateFailure',
+ { post, edits, error, options }
+ );
+ const notifyFailArgs = getNotificationArgumentsForSaveFail( {
+ post,
+ edits,
+ error,
+ } );
+ if ( notifyFailArgs.length > 0 ) {
+ yield dispatch(
+ 'core/notices',
+ 'createErrorNotice',
+ ...notifyFailArgs
+ );
+ }
+ }
}
-export function trashPost( postId, postType ) {
- return {
- type: 'TRASH_POST',
- postId,
- postType,
- };
+/**
+ * Action generator for handling refreshing the current post.
+ */
+export function* refreshPost() {
+ const post = yield select(
+ STORE_KEY,
+ 'getCurrentPost'
+ );
+ const postTypeSlug = yield select(
+ STORE_KEY,
+ 'getCurrentPostType'
+ );
+ const postType = yield resolveSelect(
+ 'core',
+ 'getPostType',
+ postTypeSlug
+ );
+ const newPost = yield apiFetch(
+ {
+ // Timestamp arg allows caller to bypass browser caching, which is
+ // expected for this specific function.
+ path: `/wp/v2/${ postType.rest_base }/${ post.id }` +
+ `?context=edit&_timestamp=${ Date.now() }`,
+ }
+ );
+ yield dispatch(
+ STORE_KEY,
+ 'resetPost',
+ newPost
+ );
}
/**
- * Returns an action object used in signalling that the post should autosave.
+ * Action generator for trashing the current post in the editor.
+ */
+export function* trashPost() {
+ const postTypeSlug = yield select(
+ STORE_KEY,
+ 'getCurrentPostType'
+ );
+ const postType = yield resolveSelect(
+ 'core',
+ 'getPostType',
+ postTypeSlug
+ );
+ yield dispatch(
+ 'core/notices',
+ 'removeNotice',
+ TRASH_POST_NOTICE_ID
+ );
+ try {
+ const post = yield select(
+ STORE_KEY,
+ 'getCurrentPost'
+ );
+ yield apiFetch(
+ {
+ path: `/wp/v2/${ postType.rest_base }/${ post.id }`,
+ method: 'DELETE',
+ }
+ );
+
+ // TODO: This should be an updatePost action (updating subsets of post
+ // properties), but right now editPost is tied with change detection.
+ yield dispatch(
+ STORE_KEY,
+ 'resetPost',
+ { ...post, status: 'trash' }
+ );
+ } catch ( error ) {
+ yield dispatch(
+ 'core/notices',
+ 'createErrorNotice',
+ ...getNotificationArgumentsForTrashFail( { error } ),
+ );
+ }
+}
+
+/**
+ * Action generator used in signalling that the post should autosave.
*
* @param {Object?} options Extra flags to identify the autosave.
- *
- * @return {Object} Action object.
*/
-export function autosave( options ) {
- return savePost( { isAutosave: true, ...options } );
+export function* autosave( options ) {
+ yield dispatch(
+ STORE_KEY,
+ 'savePost',
+ { isAutosave: true, ...options }
+ );
}
/**
@@ -264,7 +602,8 @@ export function __experimentalUpdateReusableBlockTitle( id, title ) {
}
/**
- * Returns an action object used to convert a reusable block into a static block.
+ * Returns an action object used to convert a reusable block into a static
+ * block.
*
* @param {string} clientId The client ID of the block to attach.
*
@@ -278,7 +617,8 @@ export function __experimentalConvertBlockToStatic( clientId ) {
}
/**
- * Returns an action object used to convert a static block into a reusable block.
+ * Returns an action object used to convert a static block into a reusable
+ * block.
*
* @param {string} clientIds The client IDs of the block to detach.
*
@@ -292,7 +632,8 @@ export function __experimentalConvertBlockToReusable( clientIds ) {
}
/**
- * Returns an action object used in signalling that the user has enabled the publish sidebar.
+ * Returns an action object used in signalling that the user has enabled the
+ * publish sidebar.
*
* @return {Object} Action object
*/
@@ -303,7 +644,8 @@ export function enablePublishSidebar() {
}
/**
- * Returns an action object used in signalling that the user has disabled the publish sidebar.
+ * Returns an action object used in signalling that the user has disabled the
+ * publish sidebar.
*
* @return {Object} Action object
*/
diff --git a/packages/editor/src/store/constants.js b/packages/editor/src/store/constants.js
index f07ca417f9d6e..8f8f1bd0afcef 100644
--- a/packages/editor/src/store/constants.js
+++ b/packages/editor/src/store/constants.js
@@ -7,3 +7,15 @@
export const EDIT_MERGE_PROPERTIES = new Set( [
'meta',
] );
+
+/**
+ * Constant for the store module (or reducer) key.
+ * @type {string}
+ */
+export const STORE_KEY = 'core/editor';
+
+export const POST_UPDATE_TRANSACTION_ID = 'post-update';
+export const SAVE_POST_NOTICE_ID = 'SAVE_POST_NOTICE_ID';
+export const TRASH_POST_NOTICE_ID = 'TRASH_POST_NOTICE_ID';
+export const PERMALINK_POSTNAME_REGEX = /%(?:postname|pagename)%/;
+export const ONE_MINUTE_IN_MS = 60 * 1000;
diff --git a/packages/editor/src/store/controls.js b/packages/editor/src/store/controls.js
index fc873ad43aa39..597a5f726145b 100644
--- a/packages/editor/src/store/controls.js
+++ b/packages/editor/src/store/controls.js
@@ -1,17 +1,69 @@
/**
* WordPress dependencies
*/
+import triggerFetch from '@wordpress/api-fetch';
import { createRegistryControl } from '@wordpress/data';
/**
- * Dispatches an action.
+ * Dispatches a control action for triggering an api fetch call.
*
- * @param {string} storeKey Store key.
- * @param {string} actionName Action name.
- * @param {Array} args Action arguments.
+ * @param {Object} request Arguments for the fetch request.
*
* @return {Object} control descriptor.
*/
+export function apiFetch( request ) {
+ return {
+ type: 'API_FETCH',
+ request,
+ };
+}
+
+/**
+ * Dispatches a control action for triggering a registry select.
+ *
+ * @param {string} storeKey
+ * @param {string} selectorName
+ * @param {Array} args Arguments for the select.
+ *
+ * @return {Object} control descriptor.
+ */
+export function select( storeKey, selectorName, ...args ) {
+ return {
+ type: 'SELECT',
+ storeKey,
+ selectorName,
+ args,
+ };
+}
+
+/**
+ * Dispatches a control action for triggering a registry select that has a
+ * resolver.
+ *
+ * @param {string} storeKey
+ * @param {string} selectorName
+ * @param {Array} args Arguments for the select.
+ *
+ * @return {Object} control descriptor.
+ */
+export function resolveSelect( storeKey, selectorName, ...args ) {
+ return {
+ type: 'RESOLVE_SELECT',
+ storeKey,
+ selectorName,
+ args,
+ };
+}
+
+/**
+ * Dispatches a control action for triggering a registry dispatch.
+ *
+ * @param {string} storeKey
+ * @param {string} actionName
+ * @param {Array} args Arguments for the dispatch action.
+ *
+ * @return {Object} control descriptor.
+ */
export function dispatch( storeKey, actionName, ...args ) {
return {
type: 'DISPATCH',
@@ -21,10 +73,41 @@ export function dispatch( storeKey, actionName, ...args ) {
};
}
-const controls = {
- DISPATCH: createRegistryControl( ( registry ) => ( { storeKey, actionName, args } ) => {
- return registry.dispatch( storeKey )[ actionName ]( ...args );
- } ),
-};
+export default {
+ API_FETCH( { request } ) {
+ return triggerFetch( request );
+ },
+ SELECT: createRegistryControl(
+ ( registry ) => ( { storeKey, selectorName, args } ) => {
+ return registry.select( storeKey )[ selectorName ]( ...args );
+ }
+ ),
+ DISPATCH: createRegistryControl(
+ ( registry ) => ( { storeKey, actionName, args } ) => {
+ return registry.dispatch( storeKey )[ actionName ]( ...args );
+ }
+ ),
+ RESOLVE_SELECT: createRegistryControl(
+ ( registry ) => ( { storeKey, selectorName, args } ) => {
+ return new Promise( ( resolve ) => {
+ const hasFinished = () => registry.select( 'core/data' )
+ .hasFinishedResolution( storeKey, selectorName, args );
+ const getResult = () => registry.select( storeKey )[ selectorName ]
+ .apply( null, args );
-export default controls;
+ // trigger the selector (to trigger the resolver)
+ const result = getResult();
+ if ( hasFinished() ) {
+ return resolve( result );
+ }
+
+ const unsubscribe = registry.subscribe( () => {
+ if ( hasFinished() ) {
+ unsubscribe();
+ resolve( getResult() );
+ }
+ } );
+ } );
+ }
+ ),
+};
diff --git a/packages/editor/src/store/effects.js b/packages/editor/src/store/effects.js
index de77fbc45aab5..84f5115113766 100644
--- a/packages/editor/src/store/effects.js
+++ b/packages/editor/src/store/effects.js
@@ -26,28 +26,8 @@ import {
convertBlockToStatic,
receiveReusableBlocks,
} from './effects/reusable-blocks';
-import {
- requestPostUpdate,
- requestPostUpdateSuccess,
- requestPostUpdateFailure,
- trashPost,
- trashPostFailure,
- refreshPost,
-} from './effects/posts';
export default {
- REQUEST_POST_UPDATE: ( action, store ) => {
- requestPostUpdate( action, store );
- },
- REQUEST_POST_UPDATE_SUCCESS: requestPostUpdateSuccess,
- REQUEST_POST_UPDATE_FAILURE: requestPostUpdateFailure,
- TRASH_POST: ( action, store ) => {
- trashPost( action, store );
- },
- TRASH_POST_FAILURE: trashPostFailure,
- REFRESH_POST: ( action, store ) => {
- refreshPost( action, store );
- },
SETUP_EDITOR( action ) {
const { post, edits, template } = action;
diff --git a/packages/editor/src/store/effects/posts.js b/packages/editor/src/store/effects/posts.js
deleted file mode 100644
index f9cd28fd0adbb..0000000000000
--- a/packages/editor/src/store/effects/posts.js
+++ /dev/null
@@ -1,319 +0,0 @@
-/**
- * External dependencies
- */
-import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
-import { get, pick, includes } from 'lodash';
-
-/**
- * WordPress dependencies
- */
-import apiFetch from '@wordpress/api-fetch';
-import { __ } from '@wordpress/i18n';
-// TODO: Ideally this would be the only dispatch in scope. This requires either
-// refactoring editor actions to yielded controls, or replacing direct dispatch
-// on the editor store with action creators (e.g. `REQUEST_POST_UPDATE_START`).
-import { dispatch as dataDispatch } from '@wordpress/data';
-
-/**
- * Internal dependencies
- */
-import {
- resetAutosave,
- resetPost,
- updatePost,
-} from '../actions';
-import {
- getCurrentPost,
- getPostEdits,
- getEditedPostContent,
- getAutosave,
- getCurrentPostType,
- isEditedPostSaveable,
- isEditedPostNew,
- POST_UPDATE_TRANSACTION_ID,
-} from '../selectors';
-import { resolveSelector } from './utils';
-
-/**
- * Module Constants
- */
-export const SAVE_POST_NOTICE_ID = 'SAVE_POST_NOTICE_ID';
-const TRASH_POST_NOTICE_ID = 'TRASH_POST_NOTICE_ID';
-
-/**
- * Request Post Update Effect handler
- *
- * @param {Object} action the fetchReusableBlocks action object.
- * @param {Object} store Redux Store.
- */
-export const requestPostUpdate = async ( action, store ) => {
- const { dispatch, getState } = store;
- const state = getState();
-
- // Prevent save if not saveable.
- // We don't check for dirtiness here as this can be overridden in the UI.
- if ( ! isEditedPostSaveable( state ) ) {
- return;
- }
-
- let edits = getPostEdits( state );
- const isAutosave = !! action.options.isAutosave;
- if ( isAutosave ) {
- edits = pick( edits, [ 'title', 'content', 'excerpt' ] );
- }
-
- // New posts (with auto-draft status) must be explicitly assigned draft
- // status if there is not already a status assigned in edits (publish).
- // Otherwise, they are wrongly left as auto-draft. Status is not always
- // respected for autosaves, so it cannot simply be included in the pick
- // above. This behavior relies on an assumption that an auto-draft post
- // would never be saved by anyone other than the owner of the post, per
- // logic within autosaves REST controller to save status field only for
- // draft/auto-draft by current user.
- //
- // See: https://core.trac.wordpress.org/ticket/43316#comment:88
- // See: https://core.trac.wordpress.org/ticket/43316#comment:89
- if ( isEditedPostNew( state ) ) {
- edits = { status: 'draft', ...edits };
- }
-
- const post = getCurrentPost( state );
-
- let toSend = {
- ...edits,
- content: getEditedPostContent( state ),
- id: post.id,
- };
-
- const postType = await resolveSelector( 'core', 'getPostType', getCurrentPostType( state ) );
-
- dispatch( {
- type: 'REQUEST_POST_UPDATE_START',
- optimist: { type: BEGIN, id: POST_UPDATE_TRANSACTION_ID },
- options: action.options,
- } );
-
- // Optimistically apply updates under the assumption that the post
- // will be updated. See below logic in success resolution for revert
- // if the autosave is applied as a revision.
- dispatch( {
- ...updatePost( toSend ),
- optimist: { id: POST_UPDATE_TRANSACTION_ID },
- } );
-
- let request;
- if ( isAutosave ) {
- // Ensure autosaves contain all expected fields, using autosave or
- // post values as fallback if not otherwise included in edits.
- toSend = {
- ...pick( post, [ 'title', 'content', 'excerpt' ] ),
- ...getAutosave( state ),
- ...toSend,
- };
-
- request = apiFetch( {
- path: `/wp/v2/${ postType.rest_base }/${ post.id }/autosaves`,
- method: 'POST',
- data: toSend,
- } );
- } else {
- dataDispatch( 'core/notices' ).removeNotice( SAVE_POST_NOTICE_ID );
- dataDispatch( 'core/notices' ).removeNotice( 'autosave-exists' );
-
- request = apiFetch( {
- path: `/wp/v2/${ postType.rest_base }/${ post.id }`,
- method: 'PUT',
- data: toSend,
- } );
- }
-
- try {
- const newPost = await request;
- const reset = isAutosave ? resetAutosave : resetPost;
- dispatch( reset( newPost ) );
-
- // An autosave may be processed by the server as a regular save
- // when its update is requested by the author and the post was
- // draft or auto-draft.
- const isRevision = newPost.id !== post.id;
-
- dispatch( {
- type: 'REQUEST_POST_UPDATE_SUCCESS',
- previousPost: post,
- post: newPost,
- optimist: {
- // Note: REVERT is not a failure case here. Rather, it
- // is simply reversing the assumption that the updates
- // were applied to the post proper, such that the post
- // treated as having unsaved changes.
- type: isRevision ? REVERT : COMMIT,
- id: POST_UPDATE_TRANSACTION_ID,
- },
- options: action.options,
- postType,
- } );
- } catch ( error ) {
- dispatch( {
- type: 'REQUEST_POST_UPDATE_FAILURE',
- optimist: { type: REVERT, id: POST_UPDATE_TRANSACTION_ID },
- post,
- edits,
- error,
- options: action.options,
- } );
- }
-};
-
-/**
- * Request Post Update Success Effect handler
- *
- * @param {Object} action action object.
- * @param {Object} store Redux Store.
- */
-export const requestPostUpdateSuccess = ( action ) => {
- const { previousPost, post, postType } = action;
-
- // Autosaves are neither shown a notice nor redirected.
- if ( get( action.options, [ 'isAutosave' ] ) ) {
- return;
- }
-
- const publishStatus = [ 'publish', 'private', 'future' ];
- const isPublished = includes( publishStatus, previousPost.status );
- const willPublish = includes( publishStatus, post.status );
-
- let noticeMessage;
- let shouldShowLink = get( postType, [ 'viewable' ], false );
-
- if ( ! isPublished && ! willPublish ) {
- // If saving a non-published post, don't show notice.
- noticeMessage = null;
- } else if ( isPublished && ! willPublish ) {
- // If undoing publish status, show specific notice
- noticeMessage = postType.labels.item_reverted_to_draft;
- shouldShowLink = false;
- } else if ( ! isPublished && willPublish ) {
- // If publishing or scheduling a post, show the corresponding
- // publish message
- noticeMessage = {
- publish: postType.labels.item_published,
- private: postType.labels.item_published_privately,
- future: postType.labels.item_scheduled,
- }[ post.status ];
- } else {
- // Generic fallback notice
- noticeMessage = postType.labels.item_updated;
- }
-
- if ( noticeMessage ) {
- const actions = [];
- if ( shouldShowLink ) {
- actions.push( {
- label: postType.labels.view_item,
- url: post.link,
- } );
- }
-
- dataDispatch( 'core/notices' ).createSuccessNotice(
- noticeMessage,
- {
- id: SAVE_POST_NOTICE_ID,
- actions,
- }
- );
- }
-};
-
-/**
- * Request Post Update Failure Effect handler
- *
- * @param {Object} action action object.
- */
-export const requestPostUpdateFailure = ( action ) => {
- const { post, edits, error } = action;
-
- if ( error && 'rest_autosave_no_changes' === error.code ) {
- // Autosave requested a new autosave, but there were no changes. This shouldn't
- // result in an error notice for the user.
- return;
- }
-
- const publishStatus = [ 'publish', 'private', 'future' ];
- const isPublished = publishStatus.indexOf( post.status ) !== -1;
- // If the post was being published, we show the corresponding publish error message
- // Unless we publish an "updating failed" message
- const messages = {
- publish: __( 'Publishing failed' ),
- private: __( 'Publishing failed' ),
- future: __( 'Scheduling failed' ),
- };
- const noticeMessage = ! isPublished && publishStatus.indexOf( edits.status ) !== -1 ?
- messages[ edits.status ] :
- __( 'Updating failed' );
-
- dataDispatch( 'core/notices' ).createErrorNotice( noticeMessage, {
- id: SAVE_POST_NOTICE_ID,
- } );
-};
-
-/**
- * Trash Post Effect handler
- *
- * @param {Object} action action object.
- * @param {Object} store Redux Store.
- */
-export const trashPost = async ( action, store ) => {
- const { dispatch, getState } = store;
- const { postId } = action;
- const postTypeSlug = getCurrentPostType( getState() );
- const postType = await resolveSelector( 'core', 'getPostType', postTypeSlug );
-
- dataDispatch( 'core/notices' ).removeNotice( TRASH_POST_NOTICE_ID );
- try {
- await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ postId }`, method: 'DELETE' } );
- const post = getCurrentPost( getState() );
-
- // TODO: This should be an updatePost action (updating subsets of post properties),
- // But right now editPost is tied with change detection.
- dispatch( resetPost( { ...post, status: 'trash' } ) );
- } catch ( error ) {
- dispatch( {
- ...action,
- type: 'TRASH_POST_FAILURE',
- error,
- } );
- }
-};
-
-/**
- * Trash Post Failure Effect handler
- *
- * @param {Object} action action object.
- * @param {Object} store Redux Store.
- */
-export const trashPostFailure = ( action ) => {
- const message = action.error.message && action.error.code !== 'unknown_error' ? action.error.message : __( 'Trashing failed' );
- dataDispatch( 'core/notices' ).createErrorNotice( message, {
- id: TRASH_POST_NOTICE_ID,
- } );
-};
-
-/**
- * Refresh Post Effect handler
- *
- * @param {Object} action action object.
- * @param {Object} store Redux Store.
- */
-export const refreshPost = async ( action, store ) => {
- const { dispatch, getState } = store;
-
- const state = getState();
- const post = getCurrentPost( state );
- const postTypeSlug = getCurrentPostType( getState() );
- const postType = await resolveSelector( 'core', 'getPostType', postTypeSlug );
- const newPost = await apiFetch( {
- // Timestamp arg allows caller to bypass browser caching, which is expected for this specific function.
- path: `/wp/v2/${ postType.rest_base }/${ post.id }?context=edit&_timestamp=${ Date.now() }`,
- } );
- dispatch( resetPost( newPost ) );
-};
diff --git a/packages/editor/src/store/index.js b/packages/editor/src/store/index.js
index bc7b51a604fad..42af629bcce0d 100644
--- a/packages/editor/src/store/index.js
+++ b/packages/editor/src/store/index.js
@@ -11,13 +11,9 @@ import applyMiddlewares from './middlewares';
import * as selectors from './selectors';
import * as actions from './actions';
import controls from './controls';
+import { STORE_KEY } from './constants';
-/**
- * Module Constants
- */
-const MODULE_KEY = 'core/editor';
-
-const store = registerStore( MODULE_KEY, {
+const store = registerStore( STORE_KEY, {
reducer,
selectors,
actions,
diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js
index c3147abda7063..c4d97cbd741ba 100644
--- a/packages/editor/src/store/selectors.js
+++ b/packages/editor/src/store/selectors.js
@@ -27,18 +27,12 @@ import { createRegistrySelector } from '@wordpress/data';
* Internal dependencies
*/
import { PREFERENCES_DEFAULTS } from './defaults';
-import { EDIT_MERGE_PROPERTIES } from './constants';
-
-/***
- * Module constants
- */
-export const POST_UPDATE_TRANSACTION_ID = 'post-update';
-const PERMALINK_POSTNAME_REGEX = /%(?:postname|pagename)%/;
-export const INSERTER_UTILITY_HIGH = 3;
-export const INSERTER_UTILITY_MEDIUM = 2;
-export const INSERTER_UTILITY_LOW = 1;
-export const INSERTER_UTILITY_NONE = 0;
-const ONE_MINUTE_IN_MS = 60 * 1000;
+import {
+ EDIT_MERGE_PROPERTIES,
+ POST_UPDATE_TRANSACTION_ID,
+ PERMALINK_POSTNAME_REGEX,
+ ONE_MINUTE_IN_MS,
+} from './constants';
/**
* Shared reference to an empty object for cases where it is important to avoid
@@ -124,7 +118,7 @@ export function isEditedPostDirty( state ) {
return true;
}
- // Edits and change detectiona are reset at the start of a save, but a post
+ // Edits and change detection are reset at the start of a save, but a post
// is still considered dirty until the point at which the save completes.
// Because the save is performed optimistically, the prior states are held
// until committed. These can be referenced to determine whether there's a
@@ -264,7 +258,7 @@ export function getCurrentPostAttribute( state, attributeName ) {
/**
* Returns a single attribute of the post being edited, preferring the unsaved
- * edit if one exists, but mergiging with the attribute value for the last known
+ * edit if one exists, but merging with the attribute value for the last known
* saved state of the post (this is needed for some nested attributes like meta).
*
* @param {Object} state Global application state.
diff --git a/packages/editor/src/store/test/actions.js b/packages/editor/src/store/test/actions.js
index ec46b287c394e..296a365fe4c49 100644
--- a/packages/editor/src/store/test/actions.js
+++ b/packages/editor/src/store/test/actions.js
@@ -1,26 +1,635 @@
+/**
+ * External dependencies
+ */
+import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
+
/**
* Internal dependencies
*/
+import * as actions from '../actions';
+import { select, dispatch, apiFetch, resolveSelect } from '../controls';
import {
- __experimentalFetchReusableBlocks as fetchReusableBlocks,
- __experimentalSaveReusableBlock as saveReusableBlock,
- __experimentalDeleteReusableBlock as deleteReusableBlock,
- __experimentalConvertBlockToStatic as convertBlockToStatic,
- __experimentalConvertBlockToReusable as convertBlockToReusable,
- setupEditor,
- resetPost,
- editPost,
- savePost,
- trashPost,
- redo,
- undo,
-} from '../actions';
+ STORE_KEY,
+ SAVE_POST_NOTICE_ID,
+ TRASH_POST_NOTICE_ID,
+ POST_UPDATE_TRANSACTION_ID,
+} from '../constants';
+
+jest.mock( '../controls' );
+
+select.mockImplementation( ( ...args ) => {
+ const { select: actualSelect } = jest.requireActual( '../controls' );
+ return actualSelect( ...args );
+} );
+
+dispatch.mockImplementation( ( ...args ) => {
+ const { dispatch: actualDispatch } = jest.requireActual( '../controls' );
+ return actualDispatch( ...args );
+} );
+
+resolveSelect.mockImplementation( ( ...args ) => {
+ const { resolveSelect: selectResolver } = jest
+ .requireActual( '../controls' );
+ return selectResolver( ...args );
+} );
+
+const apiFetchThrowError = ( error ) => {
+ apiFetch.mockClear();
+ apiFetch.mockImplementation( () => {
+ throw error;
+ } );
+};
+
+const apiFetchDoActual = () => {
+ apiFetch.mockClear();
+ apiFetch.mockImplementation( ( ...args ) => {
+ const { apiFetch: fetch } = jest.requireActual( '../controls' );
+ return fetch( ...args );
+ } );
+};
+
+const postType = {
+ rest_base: 'posts',
+ labels: {
+ item_updated: 'Updated Post',
+ item_published: 'Post published',
+ },
+};
+const postTypeSlug = 'post';
+
+describe( 'Post generator actions', () => {
+ describe( 'savePost()', () => {
+ let fulfillment,
+ edits,
+ currentPost,
+ currentPostStatus,
+ editPostToSendOptimistic,
+ autoSavePost,
+ autoSavePostToSend,
+ savedPost,
+ savedPostStatus,
+ isAutosave,
+ isEditedPostNew,
+ savedPostMessage;
+ beforeEach( () => {
+ edits = ( defaultStatus = null ) => {
+ const postObject = {
+ title: 'foo',
+ content: 'bar',
+ excerpt: 'cheese',
+ foo: 'bar',
+ };
+ if ( defaultStatus !== null ) {
+ postObject.status = defaultStatus;
+ }
+ return postObject;
+ };
+ currentPost = () => ( {
+ id: 44,
+ title: 'bar',
+ content: 'bar',
+ excerpt: 'crackers',
+ status: currentPostStatus,
+ } );
+ editPostToSendOptimistic = () => {
+ const postObject = {
+ ...edits(),
+ content: editedPostContent,
+ id: currentPost().id,
+ };
+ if ( ! postObject.status && isEditedPostNew ) {
+ postObject.status = 'draft';
+ }
+ if ( isAutosave ) {
+ delete postObject.foo;
+ }
+ return postObject;
+ };
+ autoSavePost = { status: 'autosave', bar: 'foo' };
+ autoSavePostToSend = () => (
+ {
+ ...editPostToSendOptimistic(),
+ bar: 'foo',
+ status: 'autosave',
+ }
+ );
+ savedPost = () => (
+ {
+ ...currentPost(),
+ ...editPostToSendOptimistic(),
+ content: editedPostContent,
+ status: savedPostStatus,
+ }
+ );
+ } );
+ const editedPostContent = 'to infinity and beyond';
+ const reset = ( isAutosaving ) => fulfillment = actions.savePost(
+ { isAutosave: isAutosaving }
+ );
+ const rewind = ( isAutosaving, isNewPost ) => {
+ reset( isAutosaving );
+ fulfillment.next();
+ fulfillment.next( true );
+ fulfillment.next( edits() );
+ fulfillment.next( isNewPost );
+ fulfillment.next( currentPost() );
+ fulfillment.next( editedPostContent );
+ fulfillment.next( postTypeSlug );
+ fulfillment.next( postType );
+ fulfillment.next();
+ if ( isAutosaving ) {
+ fulfillment.next();
+ } else {
+ fulfillment.next();
+ fulfillment.next();
+ }
+ };
+ const initialTestConditions = [
+ [
+ 'yields action for selecting if edited post is saveable',
+ () => true,
+ () => {
+ reset( isAutosave );
+ const { value } = fulfillment.next();
+ expect( value ).toEqual(
+ select( STORE_KEY, 'isEditedPostSaveable' )
+ );
+ },
+ ],
+ [
+ 'yields action for selecting the post edits done',
+ () => true,
+ () => {
+ const { value } = fulfillment.next( true );
+ expect( value ).toEqual(
+ select( STORE_KEY, 'getPostEdits' )
+ );
+ },
+ ],
+ [
+ 'yields action for selecting whether the edited post is new',
+ () => true,
+ () => {
+ const { value } = fulfillment.next( edits() );
+ expect( value ).toEqual(
+ select( STORE_KEY, 'isEditedPostNew' )
+ );
+ },
+ ],
+ [
+ 'yields action for selecting the current post',
+ () => true,
+ () => {
+ const { value } = fulfillment.next( isEditedPostNew );
+ expect( value ).toEqual(
+ select( STORE_KEY, 'getCurrentPost' )
+ );
+ },
+ ],
+ [
+ 'yields action for selecting the edited post content',
+ () => true,
+ () => {
+ const { value } = fulfillment.next( currentPost() );
+ expect( value ).toEqual(
+ select( STORE_KEY, 'getEditedPostContent' )
+ );
+ },
+ ],
+ [
+ 'yields action for selecting current post type slug',
+ () => true,
+ () => {
+ const { value } = fulfillment.next( editedPostContent );
+ expect( value ).toEqual(
+ select( STORE_KEY, 'getCurrentPostType' )
+ );
+ },
+ ],
+ [
+ 'yields action for selecting the post type object',
+ () => true,
+ () => {
+ const { value } = fulfillment.next( postTypeSlug );
+ expect( value ).toEqual(
+ resolveSelect( 'core', 'getPostType', postTypeSlug )
+ );
+ },
+ ],
+ [
+ 'yields action for dispatching request post update start',
+ () => true,
+ () => {
+ const { value } = fulfillment.next( postType );
+ expect( value ).toEqual(
+ dispatch(
+ STORE_KEY,
+ '__experimentalRequestPostUpdateStart',
+ { isAutosave }
+ )
+ );
+ },
+ ],
+ [
+ 'yields action for dispatching optimistic update of post',
+ () => true,
+ () => {
+ const { value } = fulfillment.next();
+ expect( value ).toEqual(
+ dispatch(
+ STORE_KEY,
+ '__experimentalOptimisticUpdatePost',
+ editPostToSendOptimistic()
+ )
+ );
+ },
+ ],
+ [
+ 'yields action for dispatching the removal of save post notice',
+ ( isAutosaving ) => ! isAutosaving,
+ () => {
+ const { value } = fulfillment.next();
+ expect( value ).toEqual(
+ dispatch(
+ 'core/notices',
+ 'removeNotice',
+ SAVE_POST_NOTICE_ID,
+ )
+ );
+ },
+ ],
+ [
+ 'yields action for dispatching the removal of autosave notice',
+ ( isAutosaving ) => ! isAutosaving,
+ () => {
+ const { value } = fulfillment.next();
+ expect( value ).toEqual(
+ dispatch(
+ 'core/notices',
+ 'removeNotice',
+ 'autosave-exists'
+ )
+ );
+ },
+ ],
+ [
+ 'yield action for selecting the autoSavePost',
+ ( isAutosaving ) => isAutosaving,
+ () => {
+ const { value } = fulfillment.next();
+ expect( value ).toEqual(
+ select(
+ STORE_KEY,
+ 'getAutosave'
+ )
+ );
+ },
+ ],
+ ];
+ const fetchErrorConditions = [
+ [
+ 'yields action for dispatching post update failure',
+ () => {
+ const error = { foo: 'bar', code: 'fail' };
+ apiFetchThrowError( error );
+ const editsObject = edits();
+ const { value } = isAutosave ?
+ fulfillment.next( autoSavePost ) :
+ fulfillment.next();
+ if ( isAutosave ) {
+ delete editsObject.foo;
+ }
+ expect( value ).toEqual(
+ dispatch(
+ STORE_KEY,
+ '__experimentalRequestPostUpdateFailure',
+ {
+ post: currentPost(),
+ edits: isEditedPostNew ?
+ { ...editsObject, status: 'draft' } :
+ editsObject,
+ error,
+ options: { isAutosave },
+ }
+ )
+ );
+ },
+ ],
+ [
+ 'yields action for dispatching an appropriate error notice',
+ () => {
+ const { value } = fulfillment.next( [ 'foo', 'bar' ] );
+ expect( value ).toEqual(
+ dispatch(
+ 'core/notices',
+ 'createErrorNotice',
+ ...[ 'Updating failed', { id: 'SAVE_POST_NOTICE_ID' } ]
+ )
+ );
+ },
+ ],
+ ];
+ const fetchSuccessConditions = [
+ [
+ 'yields action for updating the post via the api',
+ () => {
+ apiFetchDoActual();
+ rewind( isAutosave, isEditedPostNew );
+ const { value } = isAutosave ?
+ fulfillment.next( autoSavePost ) :
+ fulfillment.next();
+ const data = isAutosave ?
+ autoSavePostToSend() :
+ editPostToSendOptimistic();
+ const path = isAutosave ? '/autosaves' : '';
+ expect( value ).toEqual(
+ apiFetch(
+ {
+ path: `/wp/v2/${ postType.rest_base }/${ editPostToSendOptimistic().id }${ path }`,
+ method: isAutosave ? 'POST' : 'PUT',
+ data,
+ }
+ )
+ );
+ },
+ ],
+ [
+ 'yields action for dispatch the appropriate reset action',
+ () => {
+ const { value } = fulfillment.next( savedPost() );
+ expect( value ).toEqual(
+ dispatch(
+ STORE_KEY,
+ isAutosave ? 'resetAutosave' : 'resetPost',
+ savedPost()
+ )
+ );
+ },
+ ],
+ [
+ 'yields action for dispatching the post update success',
+ () => {
+ const { value } = fulfillment.next();
+ expect( value ).toEqual(
+ dispatch(
+ STORE_KEY,
+ '__experimentalRequestPostUpdateSuccess',
+ {
+ previousPost: currentPost(),
+ post: savedPost(),
+ options: { isAutosave },
+ postType,
+ isRevision: false,
+ }
+ )
+ );
+ },
+ ],
+ [
+ 'yields dispatch action for success notification',
+ () => {
+ const { value } = fulfillment.next( [ 'foo', 'bar' ] );
+ const expected = isAutosave ?
+ undefined :
+ dispatch(
+ 'core/notices',
+ 'createSuccessNotice',
+ ...[
+ savedPostMessage,
+ { actions: [], id: 'SAVE_POST_NOTICE_ID' },
+ ]
+ );
+ expect( value ).toEqual( expected );
+ },
+ ],
+ ];
+
+ const conditionalRunTestRoutine = ( isAutosaving ) => ( [
+ testDescription,
+ shouldRun,
+ testRoutine,
+ ] ) => {
+ if ( shouldRun( isAutosaving ) ) {
+ it( testDescription, () => {
+ testRoutine();
+ } );
+ }
+ };
+
+ const testRunRoutine = ( [ testDescription, testRoutine ] ) => {
+ it( testDescription, () => {
+ testRoutine();
+ } );
+ };
+
+ describe( 'yields with expected responses when edited post is not ' +
+ 'saveable', () => {
+ it( 'yields action for selecting if edited post is saveable', () => {
+ reset( false );
+ const { value } = fulfillment.next();
+ expect( value ).toEqual(
+ select( STORE_KEY, 'isEditedPostSaveable' )
+ );
+ } );
+ it( 'if edited post is not saveable then bails', () => {
+ const { value, done } = fulfillment.next( false );
+ expect( done ).toBe( true );
+ expect( value ).toBeUndefined();
+ } );
+ } );
+ describe( 'yields with expected responses for when not autosaving ' +
+ 'and edited post is new', () => {
+ beforeEach( () => {
+ isAutosave = false;
+ isEditedPostNew = true;
+ savedPostStatus = 'publish';
+ currentPostStatus = 'draft';
+ savedPostMessage = 'Post published';
+ } );
+ initialTestConditions.forEach( conditionalRunTestRoutine( false ) );
+ describe( 'fetch action throwing an error', () => {
+ fetchErrorConditions.forEach( testRunRoutine );
+ } );
+ describe( 'fetch action not throwing an error', () => {
+ fetchSuccessConditions.forEach( testRunRoutine );
+ } );
+ } );
+
+ describe( 'yields with expected responses for when not autosaving ' +
+ 'and edited post is not new', () => {
+ beforeEach( () => {
+ isAutosave = false;
+ isEditedPostNew = false;
+ currentPostStatus = 'publish';
+ savedPostStatus = 'publish';
+ savedPostMessage = 'Updated Post';
+ } );
+ initialTestConditions.forEach( conditionalRunTestRoutine( false ) );
+ describe( 'fetch action throwing error', () => {
+ fetchErrorConditions.forEach( testRunRoutine );
+ } );
+ describe( 'fetch action not throwing error', () => {
+ fetchSuccessConditions.forEach( testRunRoutine );
+ } );
+ } );
+ describe( 'yields with expected responses for when autosaving is true ' +
+ 'and edited post is not new', () => {
+ beforeEach( () => {
+ isAutosave = true;
+ isEditedPostNew = false;
+ currentPostStatus = 'autosave';
+ savedPostStatus = 'publish';
+ savedPostMessage = 'Post published';
+ } );
+ initialTestConditions.forEach( conditionalRunTestRoutine( true ) );
+ describe( 'fetch action throwing error', () => {
+ fetchErrorConditions.forEach( testRunRoutine );
+ } );
+ describe( 'fetch action not throwing error', () => {
+ fetchSuccessConditions.forEach( testRunRoutine );
+ } );
+ } );
+ } );
+ describe( 'autosave()', () => {
+ it( 'dispatches savePost with the correct arguments', () => {
+ const fulfillment = actions.autosave();
+ const { value } = fulfillment.next();
+ expect( value.actionName ).toBe( 'savePost' );
+ expect( value.args ).toEqual( [ { isAutosave: true } ] );
+ } );
+ } );
+ describe( 'trashPost()', () => {
+ let fulfillment;
+ const currentPost = { id: 10, content: 'foo', status: 'publish' };
+ const reset = () => fulfillment = actions.trashPost();
+ const rewind = () => {
+ reset();
+ fulfillment.next();
+ fulfillment.next( postTypeSlug );
+ fulfillment.next( postType );
+ fulfillment.next();
+ };
+ it( 'yields expected action for selecting the current post type slug',
+ () => {
+ reset();
+ const { value } = fulfillment.next();
+ expect( value ).toEqual( select(
+ STORE_KEY,
+ 'getCurrentPostType',
+ ) );
+ }
+ );
+ it( 'yields expected action for selecting the post type object', () => {
+ const { value } = fulfillment.next( postTypeSlug );
+ expect( value ).toEqual( resolveSelect(
+ 'core',
+ 'getPostType',
+ postTypeSlug
+ ) );
+ } );
+ it( 'yields expected action for dispatching removing the trash notice ' +
+ 'for the post', () => {
+ const { value } = fulfillment.next( postType );
+ expect( value ).toEqual( dispatch(
+ 'core/notices',
+ 'removeNotice',
+ TRASH_POST_NOTICE_ID
+ ) );
+ } );
+ it( 'yields expected action for selecting the currentPost', () => {
+ const { value } = fulfillment.next();
+ expect( value ).toEqual( select(
+ STORE_KEY,
+ 'getCurrentPost'
+ ) );
+ } );
+ describe( 'expected yields when fetch throws an error', () => {
+ it( 'yields expected action for dispatching an error notice', () => {
+ const error = { foo: 'bar', code: 'fail' };
+ apiFetchThrowError( error );
+ const { value } = fulfillment.next( currentPost );
+ expect( value ).toEqual( dispatch(
+ 'core/notices',
+ 'createErrorNotice',
+ 'Trashing failed',
+ { id: TRASH_POST_NOTICE_ID },
+ ) );
+ } );
+ } );
+ describe( 'expected yields when fetch does not throw an error', () => {
+ it( 'yields expected action object for the api fetch', () => {
+ apiFetchDoActual();
+ rewind();
+ const { value } = fulfillment.next( currentPost );
+ expect( value ).toEqual( apiFetch(
+ {
+ path: `/wp/v2/${ postType.rest_base }/${ currentPost.id }`,
+ method: 'DELETE',
+ }
+ ) );
+ } );
+ it( 'yields expected dispatch action for resetting the post', () => {
+ const { value } = fulfillment.next();
+ expect( value ).toEqual( dispatch(
+ STORE_KEY,
+ 'resetPost',
+ { ...currentPost, status: 'trash' }
+ ) );
+ } );
+ } );
+ } );
+ describe( 'refreshPost()', () => {
+ let fulfillment;
+ const currentPost = { id: 10, content: 'foo' };
+ const reset = () => fulfillment = actions.refreshPost();
+ it( 'yields expected action for selecting the currentPost', () => {
+ reset();
+ const { value } = fulfillment.next();
+ expect( value ).toEqual( select(
+ STORE_KEY,
+ 'getCurrentPost',
+ ) );
+ } );
+ it( 'yields expected action for selecting the current post type', () => {
+ const { value } = fulfillment.next( currentPost );
+ expect( value ).toEqual( select(
+ STORE_KEY,
+ 'getCurrentPostType'
+ ) );
+ } );
+ it( 'yields expected action for selecting the post type object', () => {
+ const { value } = fulfillment.next( postTypeSlug );
+ expect( value ).toEqual( resolveSelect(
+ 'core',
+ 'getPostType',
+ postTypeSlug
+ ) );
+ } );
+ it( 'yields expected action for the api fetch call', () => {
+ const { value } = fulfillment.next( postType );
+ apiFetchDoActual();
+ // since the timestamp is a computed value we can't do a direct comparison.
+ // so we'll just see if the path has most of the value.
+ expect( value.request.path ).toEqual( expect.stringContaining(
+ `/wp/v2/${ postType.rest_base }/${ currentPost.id }?context=edit&_timestamp=`
+ ) );
+ } );
+ it( 'yields expected action for dispatching the reset of the post', () => {
+ const { value } = fulfillment.next( currentPost );
+ expect( value ).toEqual( dispatch(
+ STORE_KEY,
+ 'resetPost',
+ currentPost
+ ) );
+ } );
+ } );
+} );
describe( 'actions', () => {
describe( 'setupEditor', () => {
it( 'should return the SETUP_EDITOR action', () => {
const post = {};
- const result = setupEditor( post );
+ const result = actions.setupEditor( post );
expect( result ).toEqual( {
type: 'SETUP_EDITOR',
post,
@@ -31,7 +640,7 @@ describe( 'actions', () => {
describe( 'resetPost', () => {
it( 'should return the RESET_POST action', () => {
const post = {};
- const result = resetPost( post );
+ const result = actions.resetPost( post );
expect( result ).toEqual( {
type: 'RESET_POST',
post,
@@ -39,47 +648,103 @@ describe( 'actions', () => {
} );
} );
- describe( 'editPost', () => {
- it( 'should return EDIT_POST action', () => {
- const edits = { format: 'sample' };
- expect( editPost( edits ) ).toEqual( {
- type: 'EDIT_POST',
- edits,
+ describe( 'resetAutosave', () => {
+ it( 'should return the RESET_AUTOSAVE action', () => {
+ const post = {};
+ const result = actions.resetAutosave( post );
+ expect( result ).toEqual( {
+ type: 'RESET_AUTOSAVE',
+ post,
} );
} );
} );
- describe( 'savePost', () => {
- it( 'should return REQUEST_POST_UPDATE action', () => {
- expect( savePost() ).toEqual( {
- type: 'REQUEST_POST_UPDATE',
+ describe( 'requestPostUpdateStart', () => {
+ it( 'should return the REQUEST_POST_UPDATE_START action', () => {
+ const result = actions.__experimentalRequestPostUpdateStart();
+ expect( result ).toEqual( {
+ type: 'REQUEST_POST_UPDATE_START',
+ optimist: { type: BEGIN, id: POST_UPDATE_TRANSACTION_ID },
options: {},
} );
} );
+ } );
- it( 'should pass through options argument', () => {
- expect( savePost( { autosave: true } ) ).toEqual( {
- type: 'REQUEST_POST_UPDATE',
- options: { autosave: true },
+ describe( 'requestPostUpdateSuccess', () => {
+ it( 'should return the REQUEST_POST_UPDATE_SUCCESS action', () => {
+ const testActionData = {
+ previousPost: {},
+ post: {},
+ options: {},
+ postType: 'post',
+ };
+ const result = actions.__experimentalRequestPostUpdateSuccess( {
+ ...testActionData,
+ isRevision: false,
+ } );
+ expect( result ).toEqual( {
+ ...testActionData,
+ type: 'REQUEST_POST_UPDATE_SUCCESS',
+ optimist: { type: COMMIT, id: POST_UPDATE_TRANSACTION_ID },
} );
} );
} );
- describe( 'trashPost', () => {
- it( 'should return TRASH_POST action', () => {
- const postId = 1;
- const postType = 'post';
- expect( trashPost( postId, postType ) ).toEqual( {
- type: 'TRASH_POST',
- postId,
- postType,
+ describe( 'requestPostUpdateFailure', () => {
+ it( 'should return the REQUEST_POST_UPDATE_FAILURE action', () => {
+ const testActionData = {
+ post: {},
+ options: {},
+ edits: {},
+ error: {},
+ };
+ const result = actions.__experimentalRequestPostUpdateFailure(
+ testActionData
+ );
+ expect( result ).toEqual( {
+ ...testActionData,
+ type: 'REQUEST_POST_UPDATE_FAILURE',
+ optimist: { type: REVERT, id: POST_UPDATE_TRANSACTION_ID },
+ } );
+ } );
+ } );
+
+ describe( 'updatePost', () => {
+ it( 'should return the UPDATE_POST action', () => {
+ const edits = {};
+ const result = actions.updatePost( edits );
+ expect( result ).toEqual( {
+ type: 'UPDATE_POST',
+ edits,
+ } );
+ } );
+ } );
+
+ describe( 'editPost', () => {
+ it( 'should return EDIT_POST action', () => {
+ const edits = { format: 'sample' };
+ expect( actions.editPost( edits ) ).toEqual( {
+ type: 'EDIT_POST',
+ edits,
+ } );
+ } );
+ } );
+
+ describe( 'optimisticUpdatePost', () => {
+ it( 'should return the UPDATE_POST action with optimist property', () => {
+ const edits = {};
+ const result = actions.__experimentalOptimisticUpdatePost( edits );
+ expect( result ).toEqual( {
+ type: 'UPDATE_POST',
+ edits,
+ optimist: { id: POST_UPDATE_TRANSACTION_ID },
} );
} );
} );
describe( 'redo', () => {
it( 'should return REDO action', () => {
- expect( redo() ).toEqual( {
+ expect( actions.redo() ).toEqual( {
type: 'REDO',
} );
} );
@@ -87,7 +752,7 @@ describe( 'actions', () => {
describe( 'undo', () => {
it( 'should return UNDO action', () => {
- expect( undo() ).toEqual( {
+ expect( actions.undo() ).toEqual( {
type: 'UNDO',
} );
} );
@@ -95,13 +760,13 @@ describe( 'actions', () => {
describe( 'fetchReusableBlocks', () => {
it( 'should return the FETCH_REUSABLE_BLOCKS action', () => {
- expect( fetchReusableBlocks() ).toEqual( {
+ expect( actions.__experimentalFetchReusableBlocks() ).toEqual( {
type: 'FETCH_REUSABLE_BLOCKS',
} );
} );
it( 'should take an optional id argument', () => {
- expect( fetchReusableBlocks( 123 ) ).toEqual( {
+ expect( actions.__experimentalFetchReusableBlocks( 123 ) ).toEqual( {
type: 'FETCH_REUSABLE_BLOCKS',
id: 123,
} );
@@ -110,7 +775,7 @@ describe( 'actions', () => {
describe( 'saveReusableBlock', () => {
it( 'should return the SAVE_REUSABLE_BLOCK action', () => {
- expect( saveReusableBlock( 123 ) ).toEqual( {
+ expect( actions.__experimentalSaveReusableBlock( 123 ) ).toEqual( {
type: 'SAVE_REUSABLE_BLOCK',
id: 123,
} );
@@ -119,7 +784,7 @@ describe( 'actions', () => {
describe( 'deleteReusableBlock', () => {
it( 'should return the DELETE_REUSABLE_BLOCK action', () => {
- expect( deleteReusableBlock( 123 ) ).toEqual( {
+ expect( actions.__experimentalDeleteReusableBlock( 123 ) ).toEqual( {
type: 'DELETE_REUSABLE_BLOCK',
id: 123,
} );
@@ -129,7 +794,7 @@ describe( 'actions', () => {
describe( 'convertBlockToStatic', () => {
it( 'should return the CONVERT_BLOCK_TO_STATIC action', () => {
const clientId = '358b59ee-bab3-4d6f-8445-e8c6971a5605';
- expect( convertBlockToStatic( clientId ) ).toEqual( {
+ expect( actions.__experimentalConvertBlockToStatic( clientId ) ).toEqual( {
type: 'CONVERT_BLOCK_TO_STATIC',
clientId,
} );
@@ -139,10 +804,30 @@ describe( 'actions', () => {
describe( 'convertBlockToReusable', () => {
it( 'should return the CONVERT_BLOCK_TO_REUSABLE action', () => {
const clientId = '358b59ee-bab3-4d6f-8445-e8c6971a5605';
- expect( convertBlockToReusable( clientId ) ).toEqual( {
+ expect( actions.__experimentalConvertBlockToReusable( clientId ) ).toEqual( {
type: 'CONVERT_BLOCK_TO_REUSABLE',
clientIds: [ clientId ],
} );
} );
} );
+
+ describe( 'lockPostSaving', () => {
+ it( 'should return the LOCK_POST_SAVING action', () => {
+ const result = actions.lockPostSaving( 'test' );
+ expect( result ).toEqual( {
+ type: 'LOCK_POST_SAVING',
+ lockName: 'test',
+ } );
+ } );
+ } );
+
+ describe( 'unlockPostSaving', () => {
+ it( 'should return the UNLOCK_POST_SAVING action', () => {
+ const result = actions.unlockPostSaving( 'test' );
+ expect( result ).toEqual( {
+ type: 'UNLOCK_POST_SAVING',
+ lockName: 'test',
+ } );
+ } );
+ } );
} );
diff --git a/packages/editor/src/store/test/effects.js b/packages/editor/src/store/test/effects.js
index 2e9170d2175a0..cdc7223858e0f 100644
--- a/packages/editor/src/store/test/effects.js
+++ b/packages/editor/src/store/test/effects.js
@@ -6,214 +6,17 @@ import {
unregisterBlockType,
registerBlockType,
} from '@wordpress/blocks';
-import { dispatch as dataDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
import { setupEditorState, resetEditorBlocks } from '../actions';
import effects from '../effects';
-import { SAVE_POST_NOTICE_ID } from '../effects/posts';
import '../../';
describe( 'effects', () => {
- beforeAll( () => {
- jest.spyOn( dataDispatch( 'core/notices' ), 'createErrorNotice' );
- jest.spyOn( dataDispatch( 'core/notices' ), 'createSuccessNotice' );
- } );
-
- beforeEach( () => {
- dataDispatch( 'core/notices' ).createErrorNotice.mockReset();
- dataDispatch( 'core/notices' ).createSuccessNotice.mockReset();
- } );
-
const defaultBlockSettings = { save: () => 'Saved', category: 'common', title: 'block title' };
- describe( '.REQUEST_POST_UPDATE_SUCCESS', () => {
- const handler = effects.REQUEST_POST_UPDATE_SUCCESS;
-
- const defaultPost = {
- id: 1,
- title: {
- raw: 'A History of Pork',
- },
- content: {
- raw: '',
- },
- };
- const getDraftPost = () => ( {
- ...defaultPost,
- status: 'draft',
- } );
- const getPublishedPost = () => ( {
- ...defaultPost,
- status: 'publish',
- } );
- const getPostType = () => ( {
- labels: {
- view_item: 'View post',
- item_published: 'Post published.',
- item_reverted_to_draft: 'Post reverted to draft.',
- item_updated: 'Post updated.',
- },
- viewable: true,
- } );
-
- it( 'should dispatch notices when publishing or scheduling a post', () => {
- const previousPost = getDraftPost();
- const post = getPublishedPost();
- const postType = getPostType();
-
- handler( { post, previousPost, postType } );
-
- expect( dataDispatch( 'core/notices' ).createSuccessNotice ).toHaveBeenCalledWith(
- 'Post published.',
- {
- id: SAVE_POST_NOTICE_ID,
- actions: [
- { label: 'View post', url: undefined },
- ],
- }
- );
- } );
-
- it( 'should dispatch notices when publishing or scheduling an unviewable post', () => {
- const previousPost = getDraftPost();
- const post = getPublishedPost();
- const postType = { ...getPostType(), viewable: false };
-
- handler( { post, previousPost, postType } );
-
- expect( dataDispatch( 'core/notices' ).createSuccessNotice ).toHaveBeenCalledWith(
- 'Post published.',
- {
- id: SAVE_POST_NOTICE_ID,
- actions: [],
- }
- );
- } );
-
- it( 'should dispatch notices when reverting a published post to a draft', () => {
- const previousPost = getPublishedPost();
- const post = getDraftPost();
- const postType = getPostType();
-
- handler( { post, previousPost, postType } );
-
- expect( dataDispatch( 'core/notices' ).createSuccessNotice ).toHaveBeenCalledWith(
- 'Post reverted to draft.',
- {
- id: SAVE_POST_NOTICE_ID,
- actions: [],
- }
- );
- } );
-
- it( 'should dispatch notices when just updating a published post again', () => {
- const previousPost = getPublishedPost();
- const post = getPublishedPost();
- const postType = getPostType();
-
- handler( { post, previousPost, postType } );
-
- expect( dataDispatch( 'core/notices' ).createSuccessNotice ).toHaveBeenCalledWith(
- 'Post updated.',
- {
- id: SAVE_POST_NOTICE_ID,
- actions: [
- { label: 'View post', url: undefined },
- ],
- }
- );
- } );
-
- it( 'should do nothing if the updated post was autosaved', () => {
- const previousPost = getPublishedPost();
- const post = { ...getPublishedPost(), id: defaultPost.id + 1 };
-
- handler( { post, previousPost, options: { isAutosave: true } } );
-
- expect( dataDispatch( 'core/notices' ).createSuccessNotice ).not.toHaveBeenCalled();
- } );
- } );
-
- describe( '.REQUEST_POST_UPDATE_FAILURE', () => {
- it( 'should dispatch a notice on failure when publishing a draft fails.', () => {
- const handler = effects.REQUEST_POST_UPDATE_FAILURE;
-
- const action = {
- post: {
- id: 1,
- title: {
- raw: 'A History of Pork',
- },
- content: {
- raw: '',
- },
- status: 'draft',
- },
- edits: {
- status: 'publish',
- },
- };
-
- handler( action );
-
- expect( dataDispatch( 'core/notices' ).createErrorNotice ).toHaveBeenCalledWith( 'Publishing failed', { id: SAVE_POST_NOTICE_ID } );
- } );
-
- it( 'should not dispatch a notice when there were no changes for autosave to save.', () => {
- const handler = effects.REQUEST_POST_UPDATE_FAILURE;
-
- const action = {
- post: {
- id: 1,
- title: {
- raw: 'A History of Pork',
- },
- content: {
- raw: '',
- },
- status: 'draft',
- },
- edits: {
- status: 'publish',
- },
- error: {
- code: 'rest_autosave_no_changes',
- },
- };
-
- handler( action );
-
- expect( dataDispatch( 'core/notices' ).createErrorNotice ).not.toHaveBeenCalled();
- } );
-
- it( 'should dispatch a notice on failure when trying to update a draft.', () => {
- const handler = effects.REQUEST_POST_UPDATE_FAILURE;
-
- const action = {
- post: {
- id: 1,
- title: {
- raw: 'A History of Pork',
- },
- content: {
- raw: '',
- },
- status: 'draft',
- },
- edits: {
- status: 'draft',
- },
- };
-
- handler( action );
-
- expect( dataDispatch( 'core/notices' ).createErrorNotice ).toHaveBeenCalledWith( 'Updating failed', { id: SAVE_POST_NOTICE_ID } );
- } );
- } );
-
describe( '.SETUP_EDITOR', () => {
const handler = effects.SETUP_EDITOR;
diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js
index 459161ee03801..01f2d09199e52 100644
--- a/packages/editor/src/store/test/selectors.js
+++ b/packages/editor/src/store/test/selectors.js
@@ -22,6 +22,7 @@ import { RawHTML } from '@wordpress/element';
*/
import * as selectors from '../selectors';
import { PREFERENCES_DEFAULTS } from '../defaults';
+import { POST_UPDATE_TRANSACTION_ID } from '../constants';
const {
hasEditorUndo,
@@ -64,7 +65,6 @@ const {
getStateBeforeOptimisticTransaction,
isPublishingPost,
isPublishSidebarEnabled,
- POST_UPDATE_TRANSACTION_ID,
isPermalinkEditable,
getPermalink,
getPermalinkParts,
diff --git a/packages/editor/src/store/utils/notice-builder.js b/packages/editor/src/store/utils/notice-builder.js
new file mode 100644
index 0000000000000..4ef98c74e3a54
--- /dev/null
+++ b/packages/editor/src/store/utils/notice-builder.js
@@ -0,0 +1,123 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { SAVE_POST_NOTICE_ID, TRASH_POST_NOTICE_ID } from '../constants';
+
+/**
+ * External dependencies
+ */
+import { get, includes } from 'lodash';
+
+/**
+ * Builds the arguments for a success notification dispatch.
+ *
+ * @param {Object} data Incoming data to build the arguments from.
+ *
+ * @return {Array} Arguments for dispatch. An empty array signals no
+ * notification should be sent.
+ */
+export function getNotificationArgumentsForSaveSuccess( data ) {
+ const { previousPost, post, postType } = data;
+ // Autosaves are neither shown a notice nor redirected.
+ if ( get( data.options, [ 'isAutosave' ] ) ) {
+ return [];
+ }
+
+ const publishStatus = [ 'publish', 'private', 'future' ];
+ const isPublished = includes( publishStatus, previousPost.status );
+ const willPublish = includes( publishStatus, post.status );
+
+ let noticeMessage;
+ let shouldShowLink = get( postType, [ 'viewable' ], false );
+
+ if ( ! isPublished && ! willPublish ) {
+ // If saving a non-published post, don't show notice.
+ noticeMessage = null;
+ } else if ( isPublished && ! willPublish ) {
+ // If undoing publish status, show specific notice
+ noticeMessage = postType.labels.item_reverted_to_draft;
+ shouldShowLink = false;
+ } else if ( ! isPublished && willPublish ) {
+ // If publishing or scheduling a post, show the corresponding
+ // publish message
+ noticeMessage = {
+ publish: postType.labels.item_published,
+ private: postType.labels.item_published_privately,
+ future: postType.labels.item_scheduled,
+ }[ post.status ];
+ } else {
+ // Generic fallback notice
+ noticeMessage = postType.labels.item_updated;
+ }
+
+ if ( noticeMessage ) {
+ const actions = [];
+ if ( shouldShowLink ) {
+ actions.push( {
+ label: postType.labels.view_item,
+ url: post.link,
+ } );
+ }
+ return [
+ noticeMessage,
+ {
+ id: SAVE_POST_NOTICE_ID,
+ actions,
+ },
+ ];
+ }
+ return [];
+}
+
+/**
+ * Builds the fail notification arguments for dispatch.
+ *
+ * @param {Object} data Incoming data to build the arguments with.
+ *
+ * @return {Array} Arguments for dispatch. An empty array signals no
+ * notification should be sent.
+ */
+export function getNotificationArgumentsForSaveFail( data ) {
+ const { post, edits, error } = data;
+ if ( error && 'rest_autosave_no_changes' === error.code ) {
+ // Autosave requested a new autosave, but there were no changes. This shouldn't
+ // result in an error notice for the user.
+ return [];
+ }
+
+ const publishStatus = [ 'publish', 'private', 'future' ];
+ const isPublished = publishStatus.indexOf( post.status ) !== -1;
+ // If the post was being published, we show the corresponding publish error message
+ // Unless we publish an "updating failed" message
+ const messages = {
+ publish: __( 'Publishing failed' ),
+ private: __( 'Publishing failed' ),
+ future: __( 'Scheduling failed' ),
+ };
+ const noticeMessage = ! isPublished && publishStatus.indexOf( edits.status ) !== -1 ?
+ messages[ edits.status ] :
+ __( 'Updating failed' );
+
+ return [ noticeMessage, { id: SAVE_POST_NOTICE_ID } ];
+}
+
+/**
+ * Builds the trash fail notification arguments for dispatch.
+ *
+ * @param {Object} data
+ *
+ * @return {Array} Arguments for dispatch.
+ */
+export function getNotificationArgumentsForTrashFail( data ) {
+ return [
+ data.error.message && data.error.code !== 'unknown_error' ?
+ data.error.message :
+ __( 'Trashing failed' ),
+ { id: TRASH_POST_NOTICE_ID },
+ ];
+}
diff --git a/packages/editor/src/store/utils/test/notice-builder.js b/packages/editor/src/store/utils/test/notice-builder.js
new file mode 100644
index 0000000000000..a78d03f81fad7
--- /dev/null
+++ b/packages/editor/src/store/utils/test/notice-builder.js
@@ -0,0 +1,182 @@
+/**
+ * Internal dependencies
+ */
+import {
+ getNotificationArgumentsForSaveSuccess,
+ getNotificationArgumentsForSaveFail,
+ getNotificationArgumentsForTrashFail,
+} from '../notice-builder';
+import {
+ SAVE_POST_NOTICE_ID,
+ TRASH_POST_NOTICE_ID,
+} from '../../constants';
+
+describe( 'getNotificationArgumentsForSaveSuccess()', () => {
+ const postType = {
+ labels: {
+ item_reverted_to_draft: 'draft',
+ item_published: 'publish',
+ item_published_privately: 'private',
+ item_scheduled: 'scheduled',
+ item_updated: 'updated',
+ view_item: 'view',
+ },
+ viewable: false,
+ };
+ const previousPost = {
+ status: 'publish',
+ link: 'some_link',
+ };
+ const post = { ...previousPost };
+ const defaultExpectedAction = { id: SAVE_POST_NOTICE_ID, actions: [] };
+ [
+ [
+ 'when previous post is not published and post will not be published',
+ [ 'draft', 'draft', false ],
+ [],
+ ],
+ [
+ 'when previous post is published and post will be unpublished',
+ [ 'publish', 'draft', false ],
+ [ 'draft', defaultExpectedAction ],
+ ],
+ [
+ 'when previous post is not published and post will be published',
+ [ 'draft', 'publish', false ],
+ [ 'publish', defaultExpectedAction ],
+ ],
+ [
+ 'when previous post is not published and post will be privately ' +
+ 'published',
+ [ 'draft', 'private', false ],
+ [ 'private', defaultExpectedAction ],
+ ],
+ [
+ 'when previous post is not published and post will be scheduled for ' +
+ 'publishing',
+ [ 'draft', 'future', false ],
+ [ 'scheduled', defaultExpectedAction ],
+ ],
+ [
+ 'when both are considered published',
+ [ 'private', 'publish', false ],
+ [ 'updated', defaultExpectedAction ],
+ ],
+ [
+ 'when both are considered published and the post type is viewable',
+ [ 'private', 'publish', true ],
+ [
+ 'updated',
+ {
+ ...defaultExpectedAction,
+ actions: [ { label: 'view', url: 'some_link' } ],
+ },
+ ],
+ ],
+ ].forEach( ( [
+ description,
+ [ previousPostStatus, postStatus, isViewable ],
+ expectedValue,
+ ] ) => {
+ it( description, () => {
+ previousPost.status = previousPostStatus;
+ post.status = postStatus;
+ postType.viewable = isViewable;
+ expect( getNotificationArgumentsForSaveSuccess(
+ {
+ previousPost,
+ post,
+ postType,
+ }
+ ) ).toEqual( expectedValue );
+ } );
+ } );
+} );
+describe( 'getNotificationArgumentsForSaveFail()', () => {
+ const error = { code: '42' };
+ const post = { status: 'publish' };
+ const edits = { status: 'publish' };
+ const defaultExpectedAction = { id: SAVE_POST_NOTICE_ID };
+ [
+ [
+ 'when error code is `rest_autosave_no_changes`',
+ 'rest_autosave_no_changes',
+ [ 'publish', 'publish' ],
+ [],
+ ],
+ [
+ 'when post is not published and edits is published',
+ '',
+ [ 'draft', 'publish' ],
+ [ 'Publishing failed', defaultExpectedAction ],
+ ],
+ [
+ 'when post is published and edits is privately published',
+ '',
+ [ 'draft', 'private' ],
+ [ 'Publishing failed', defaultExpectedAction ],
+ ],
+ [
+ 'when post is published and edits is scheduled to be published',
+ '',
+ [ 'draft', 'future' ],
+ [ 'Scheduling failed', defaultExpectedAction ],
+ ],
+ [
+ 'when post is published and edits is published',
+ '',
+ [ 'publish', 'publish' ],
+ [ 'Updating failed', defaultExpectedAction ],
+ ],
+ ].forEach( ( [
+ description,
+ errorCode,
+ [ postStatus, editsStatus ],
+ expectedValue,
+ ] ) => {
+ it( description, () => {
+ post.status = postStatus;
+ error.code = errorCode;
+ edits.status = editsStatus;
+ expect( getNotificationArgumentsForSaveFail(
+ {
+ post,
+ edits,
+ error,
+ }
+ ) ).toEqual( expectedValue );
+ } );
+ } );
+} );
+describe( 'getNotificationArgumentsForTrashFail()', () => {
+ [
+ [
+ 'when there is an error message and the error code is not "unknown_error"',
+ { message: 'foo', code: '' },
+ 'foo',
+ ],
+ [
+ 'when there is an error message and the error code is "unknown error"',
+ { message: 'foo', code: 'unknown_error' },
+ 'Trashing failed',
+ ],
+ [
+ 'when there is not an error message',
+ { code: 42 },
+ 'Trashing failed',
+ ],
+ ].forEach( ( [
+ description,
+ error,
+ message,
+ ] ) => {
+ it( description, () => {
+ const expectedValue = [
+ message,
+ { id: TRASH_POST_NOTICE_ID },
+ ];
+ expect( getNotificationArgumentsForTrashFail( { error } ) )
+ .toEqual( expectedValue );
+ } );
+ } );
+} );
From 29bff8571b1338aa8c909859f026d7bd9c50739e Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Thu, 28 Feb 2019 20:44:41 +0000
Subject: [PATCH 044/169] Add array-callback-return rule; Fix current code
breaking the rule. (#14154)
## Description
Adds a rule to make sure Array functions that iterate on the array and should return something contain the return statement, otherwise, a forEach should probably be used instead.
A case like this was fixed at https://github.com/WordPress/gutenberg/pull/13953.
In PR https://github.com/WordPress/gutenberg/pull/13953 @aduth suggested the implementation of a lint rule to catch these cases. While trying to implement the rule and researching the best ways to do it, I noticed a rule like that already existed in the community and this PR is enabling it.
We are also changing the code to respect the new rule no observable changes should be expected.
## How has this been tested?
Observe the tests pass.
Do some smoke testing, adding blocks, uploading files, and verify everything still works as before.
---
docs/tool/manifest.js | 2 +-
packages/block-library/src/file/index.js | 2 +-
packages/edit-post/src/hooks/components/media-upload/index.js | 2 +-
packages/eslint-plugin/CHANGELOG.md | 1 +
packages/eslint-plugin/configs/es5.js | 1 +
packages/scripts/scripts/check-licenses.js | 1 +
6 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/docs/tool/manifest.js b/docs/tool/manifest.js
index 56f7a2c8772c4..f21130e448e11 100644
--- a/docs/tool/manifest.js
+++ b/docs/tool/manifest.js
@@ -70,7 +70,7 @@ function getRootManifest( tocFileName ) {
function generateRootManifestFromTOCItems( items, parent = null ) {
let pageItems = [];
- items.map( ( obj ) => {
+ items.forEach( ( obj ) => {
const fileName = Object.keys( obj )[ 0 ];
const children = obj[ fileName ];
diff --git a/packages/block-library/src/file/index.js b/packages/block-library/src/file/index.js
index 1c7087039a5cc..9ce871bcfa277 100644
--- a/packages/block-library/src/file/index.js
+++ b/packages/block-library/src/file/index.js
@@ -86,7 +86,7 @@ export const settings = {
transform: ( files ) => {
const blocks = [];
- files.map( ( file ) => {
+ files.forEach( ( file ) => {
const blobURL = createBlobURL( file );
// File will be uploaded in componentDidMount()
diff --git a/packages/edit-post/src/hooks/components/media-upload/index.js b/packages/edit-post/src/hooks/components/media-upload/index.js
index 0b4fe40ce4ea9..bcf4d49d700c5 100644
--- a/packages/edit-post/src/hooks/components/media-upload/index.js
+++ b/packages/edit-post/src/hooks/components/media-upload/index.js
@@ -172,7 +172,7 @@ class MediaUpload extends Component {
}
if ( ! this.props.gallery ) {
const selection = this.frame.state().get( 'selection' );
- castArray( this.props.value ).map( ( id ) => {
+ castArray( this.props.value ).forEach( ( id ) => {
selection.add( wp.media.attachment( id ) );
} );
}
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index 4e3f6f8430996..0d563005b9613 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -3,6 +3,7 @@
### Breaking Changes
- The `esnext` and `recommended` rulesets now enforce [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand)
+- The `es5` and `recommended` rulesets now enforce [`array-callback-return`](https://eslint.org/docs/rules/array-callback-return)
### New Features
diff --git a/packages/eslint-plugin/configs/es5.js b/packages/eslint-plugin/configs/es5.js
index 167e542ef69a6..a13168d1223e0 100644
--- a/packages/eslint-plugin/configs/es5.js
+++ b/packages/eslint-plugin/configs/es5.js
@@ -1,6 +1,7 @@
module.exports = {
rules: {
'array-bracket-spacing': [ 'error', 'always' ],
+ 'array-callback-return': 'error',
'brace-style': [ 'error', '1tbs' ],
camelcase: [ 'error', {
properties: 'never',
diff --git a/packages/scripts/scripts/check-licenses.js b/packages/scripts/scripts/check-licenses.js
index 2120d0cbfe75e..2bc95d51e0913 100644
--- a/packages/scripts/scripts/check-licenses.js
+++ b/packages/scripts/scripts/check-licenses.js
@@ -258,6 +258,7 @@ modules.forEach( ( path ) => {
}, stringDetectedType );
}, detectedType );
}
+ return detectedType;
}, false );
}
From 18d6dee1861e5edf5e6f61d7a1098e4fe48b06f5 Mon Sep 17 00:00:00 2001
From: Brent Swisher
Date: Thu, 28 Feb 2019 16:01:16 -0500
Subject: [PATCH 045/169] Update text displayed when an embed can't be
previewed (#13715)
* Update text displayed when an embed can't be previewed
* Add translator note for update to embedded content np preview message
---
packages/block-library/src/embed/embed-preview.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/packages/block-library/src/embed/embed-preview.js b/packages/block-library/src/embed/embed-preview.js
index cf8d35460ae3d..1b1f2dfb93787 100644
--- a/packages/block-library/src/embed/embed-preview.js
+++ b/packages/block-library/src/embed/embed-preview.js
@@ -97,7 +97,12 @@ class EmbedPreview extends Component {
{ ( cannotPreview ) ? (
} label={ label }>
{ url }
- { __( 'Sorry, this embedded content cannot be previewed in the editor.' ) }
+
+ {
+ /* translators: %s: host providing embed content e.g: www.youtube.com */
+ sprintf( __( "Embedded content from %s can't be previewed in the editor." ), parsedHostBaseUrl )
+ }
+
) : embedWrapper }
{ ( ! RichText.isEmpty( caption ) || isSelected ) && (
From 77a945c86ccc209dccd14eea617ccd3bcaed70ae Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Thu, 28 Feb 2019 21:19:26 +0000
Subject: [PATCH 046/169] Fix: Image that is uploaded to an existing gallery
does not appear in the edit gallery view (#12435)
---
.../hooks/components/media-upload/index.js | 68 ++++++++++++++-----
1 file changed, 52 insertions(+), 16 deletions(-)
diff --git a/packages/edit-post/src/hooks/components/media-upload/index.js b/packages/edit-post/src/hooks/components/media-upload/index.js
index bcf4d49d700c5..df65411e1c305 100644
--- a/packages/edit-post/src/hooks/components/media-upload/index.js
+++ b/packages/edit-post/src/hooks/components/media-upload/index.js
@@ -82,7 +82,6 @@ class MediaUpload extends Component {
gallery = false,
title = __( 'Select or Upload Media' ),
modalClass,
- value,
} ) {
super( ...arguments );
this.openModal = this.openModal.bind( this );
@@ -92,21 +91,7 @@ class MediaUpload extends Component {
this.onClose = this.onClose.bind( this );
if ( gallery ) {
- const currentState = value ? 'gallery-edit' : 'gallery';
- const GalleryDetailsMediaFrame = getGalleryDetailsMediaFrame();
- const attachments = getAttachmentsCollection( value );
- const selection = new wp.media.model.Selection( attachments.models, {
- props: attachments.props.toJSON(),
- multiple,
- } );
- this.frame = new GalleryDetailsMediaFrame( {
- mimeType: allowedTypes,
- state: currentState,
- multiple,
- selection,
- editing: ( value ) ? true : false,
- } );
- wp.media.frame = this.frame;
+ this.buildAndSetGalleryFrame();
} else {
const frameConfig = {
title,
@@ -126,6 +111,10 @@ class MediaUpload extends Component {
this.frame.$el.addClass( modalClass );
}
+ this.initializeListeners();
+ }
+
+ initializeListeners() {
// When an image is selected in the media frame...
this.frame.on( 'select', this.onSelect );
this.frame.on( 'update', this.onUpdate );
@@ -133,6 +122,45 @@ class MediaUpload extends Component {
this.frame.on( 'close', this.onClose );
}
+ buildAndSetGalleryFrame() {
+ const {
+ allowedTypes,
+ multiple = false,
+ value = null,
+ } = this.props;
+ // If the value did not changed there is no need to rebuild the frame,
+ // we can continue to use the existing one.
+ if ( value === this.lastGalleryValue ) {
+ return;
+ }
+
+ this.lastGalleryValue = value;
+
+ // If a frame already existed remove it.
+ if ( this.frame ) {
+ this.frame.remove();
+ }
+ const currentState = value ? 'gallery-edit' : 'gallery';
+ if ( ! this.GalleryDetailsMediaFrame ) {
+ this.GalleryDetailsMediaFrame = getGalleryDetailsMediaFrame();
+ }
+
+ const attachments = getAttachmentsCollection( value );
+ const selection = new wp.media.model.Selection( attachments.models, {
+ props: attachments.props.toJSON(),
+ multiple,
+ } );
+ this.frame = new this.GalleryDetailsMediaFrame( {
+ mimeType: allowedTypes,
+ state: currentState,
+ multiple,
+ selection,
+ editing: ( value ) ? true : false,
+ } );
+ wp.media.frame = this.frame;
+ this.initializeListeners();
+ }
+
componentWillUnmount() {
this.frame.remove();
}
@@ -176,6 +204,7 @@ class MediaUpload extends Component {
selection.add( wp.media.attachment( id ) );
} );
}
+
// load the images so they are available in the media modal.
getAttachmentsCollection( castArray( this.props.value ) ).more();
}
@@ -205,6 +234,13 @@ class MediaUpload extends Component {
}
openModal() {
+ if (
+ this.props.gallery &&
+ this.props.value &&
+ this.props.value.length > 0
+ ) {
+ this.buildAndSetGalleryFrame();
+ }
this.frame.open();
}
From 535e0752051957f3e6ab7aca2e71e99ceffc9a6f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ella=20Van=C2=A0Durpe?=
Date: Fri, 1 Mar 2019 13:41:12 +0100
Subject: [PATCH 047/169] RichText: fix wordwise selection on Windows (#14184)
* RichText: fix wordwise selection on Windows
* Fix crtl ctrl typo.
* Add docs
---
packages/editor/src/components/rich-text/index.js | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js
index ccfe32326d944..d6be0a622307e 100644
--- a/packages/editor/src/components/rich-text/index.js
+++ b/packages/editor/src/components/rich-text/index.js
@@ -573,13 +573,14 @@ export class RichText extends Component {
/**
* Handles a keydown event.
*
- * @param {KeyboardEvent} event The keydown event.
+ * @param {SyntheticEvent} event A synthetic keyboard event.
*/
onKeyDown( event ) {
- const { keyCode, shiftKey, altKey, metaKey } = event;
+ const { keyCode, shiftKey, altKey, metaKey, ctrlKey } = event;
if (
- ! shiftKey && ! altKey && ! metaKey &&
+ // Only override left and right keys without modifiers pressed.
+ ! shiftKey && ! altKey && ! metaKey && ! ctrlKey &&
( keyCode === LEFT || keyCode === RIGHT )
) {
this.handleHorizontalNavigation( event );
@@ -661,6 +662,13 @@ export class RichText extends Component {
}
}
+ /**
+ * Handles horizontal keyboard navigation when no modifiers are pressed. The
+ * navigation is handled separately to move correctly around format
+ * boundaries.
+ *
+ * @param {SyntheticEvent} event A synthetic keyboard event.
+ */
handleHorizontalNavigation( event ) {
const value = this.createRecord();
const { formats, text, start, end } = value;
From a3b722d1e43c9a46c97fe7fc35557c3d462b199d Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Fri, 1 Mar 2019 16:24:57 +0000
Subject: [PATCH 048/169] Chore: Update: Code Quality: Remove some editor
store references from block-editor (#14161)
We missed to update the editor references from selectPreviousBlock, and selectNextBlock.
## Tests
I added multiple blocks, I verified that when I remove a block the previous block still gets selected.
---
packages/block-editor/src/store/actions.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
index 382a25f438d34..a94a526f4a5da 100644
--- a/packages/block-editor/src/store/actions.js
+++ b/packages/block-editor/src/store/actions.js
@@ -107,7 +107,7 @@ export function selectBlock( clientId, initialPosition = null ) {
*/
export function* selectPreviousBlock( clientId ) {
const previousBlockClientId = yield select(
- 'core/editor',
+ 'core/block-editor',
'getPreviousBlockClientId',
clientId
);
@@ -123,7 +123,7 @@ export function* selectPreviousBlock( clientId ) {
*/
export function* selectNextBlock( clientId ) {
const nextBlockClientId = yield select(
- 'core/editor',
+ 'core/block-editor',
'getNextBlockClientId',
clientId
);
From 8ddfa589e095b64e7d6fd7728311e0fe5cb7be4d Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Fri, 1 Mar 2019 11:53:55 -0500
Subject: [PATCH 049/169] Framework: Update package-lock.json for new Enzyme
adapter (#14192)
---
package-lock.json | 109 +++++++++++++++++++++++-----------------------
1 file changed, 54 insertions(+), 55 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index b9bda6a34c73e..1b825c2ffbb3a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2897,6 +2897,60 @@
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.10.0",
"enzyme-to-json": "^3.3.5"
+ },
+ "dependencies": {
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "enzyme-adapter-react-16": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.10.0.tgz",
+ "integrity": "sha512-0QqwEZcBv1xEEla+a3H7FMci+y4ybLia9cZzsdIrId7qcig4MK0kqqf6iiCILH1lsKS6c6AVqL3wGPhCevv5aQ==",
+ "dev": true,
+ "requires": {
+ "enzyme-adapter-utils": "^1.10.0",
+ "object.assign": "^4.1.0",
+ "object.values": "^1.1.0",
+ "prop-types": "^15.6.2",
+ "react-is": "^16.7.0",
+ "react-test-renderer": "^16.0.0-0"
+ }
+ },
+ "object.values": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+ "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ },
+ "react-is": {
+ "version": "16.8.3",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz",
+ "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==",
+ "dev": true
+ }
}
},
"@wordpress/jest-puppeteer-axe": {
@@ -7335,61 +7389,6 @@
"string.prototype.trim": "^1.1.2"
}
},
- "enzyme-adapter-react-16": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz",
- "integrity": "sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg==",
- "dev": true,
- "requires": {
- "enzyme-adapter-utils": "^1.10.0",
- "function.prototype.name": "^1.1.0",
- "object.assign": "^4.1.0",
- "object.values": "^1.1.0",
- "prop-types": "^15.6.2",
- "react-is": "^16.7.0",
- "react-test-renderer": "^16.0.0-0"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "object.values": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
- "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.12.0",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "prop-types": {
- "version": "15.7.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
- "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.8.1"
- }
- },
- "react-is": {
- "version": "16.8.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz",
- "integrity": "sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q==",
- "dev": true
- }
- }
- },
"enzyme-adapter-utils": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz",
From 468393c7c0901e554aebb80c2fd5d1acb0ea3840 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Fri, 1 Mar 2019 12:08:25 -0500
Subject: [PATCH 050/169] Notices: Remove inaccurate createNotice sole argument
feature (#14177)
---
packages/notices/CHANGELOG.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md
index 02d757332e51e..3ceb48ea97a3f 100644
--- a/packages/notices/CHANGELOG.md
+++ b/packages/notices/CHANGELOG.md
@@ -6,7 +6,6 @@
### New Feature
-- The `createNotice` can now optionally accept a WPNotice object as the sole argument.
- New option `speak` enables control as to whether the notice content is announced to screen readers (defaults to `true`)
### Bug Fixes
From 8f451a017b9a020917da37fac49a62885ca20b05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9s?=
Date: Fri, 1 Mar 2019 18:15:02 +0100
Subject: [PATCH 051/169] New package to auto-generate public API documentation
(#13329)
---
docs/manifest.json | 6 +
package-lock.json | 26 +
package.json | 2 +
packages/docgen/.npmrc | 1 +
packages/docgen/CHANGELOG.md | 3 +
packages/docgen/README.md | 250 ++++++++
packages/docgen/bin/cli.js | 44 ++
packages/docgen/coverage.md | 83 +++
packages/docgen/package.json | 33 +
packages/docgen/src/engine.js | 56 ++
packages/docgen/src/get-dependency-path.js | 3 +
packages/docgen/src/get-export-entries.js | 97 +++
.../src/get-intermediate-representation.js | 152 +++++
packages/docgen/src/get-jsdoc-from-token.js | 37 ++
packages/docgen/src/get-leading-comments.js | 20 +
packages/docgen/src/get-type-as-string.js | 44 ++
packages/docgen/src/index.js | 126 ++++
packages/docgen/src/markdown/embed.js | 51 ++
packages/docgen/src/markdown/formatter.js | 128 ++++
packages/docgen/src/markdown/index.js | 44 ++
packages/docgen/src/test/engine.js | 11 +
.../fixtures/default-class-anonymous/code.js | 4 +
.../default-class-anonymous/exports.json | 82 +++
.../test/fixtures/default-class-named/code.js | 4 +
.../fixtures/default-class-named/exports.json | 101 +++
.../default-function-anonymous/code.js | 4 +
.../default-function-anonymous/exports.json | 85 +++
.../fixtures/default-function-named/ast.json | 148 +++++
.../fixtures/default-function-named/code.js | 4 +
.../default-function-named/exports.json | 104 +++
.../fixtures/default-function-named/ir.json | 7 +
.../test/fixtures/default-identifier/ast.json | 165 +++++
.../test/fixtures/default-identifier/code.js | 6 +
.../fixtures/default-identifier/exports.json | 39 ++
.../fixtures/default-import-default/ast.json | 143 +++++
.../fixtures/default-import-default/code.js | 6 +
.../default-import-default/exports.json | 39 ++
.../default-import-default/module-code.js | 6 +
.../default-import-default/module-ir.json | 7 +
.../fixtures/default-import-named/ast.json | 163 +++++
.../fixtures/default-import-named/code.js | 6 +
.../default-import-named/exports.json | 39 ++
.../default-import-named/module-code.js | 6 +
.../default-import-named/module-ir.json | 1 +
.../fixtures/default-named-export/ast.json | 189 ++++++
.../fixtures/default-named-export/code.js | 6 +
.../default-named-export/exports.json | 147 +++++
.../default-undocumented-nocomments/code.js | 3 +
.../exports.json | 39 ++
.../default-undocumented-oneliner/code.js | 2 +
.../exports.json | 85 +++
.../test/fixtures/default-variable/code.js | 4 +
.../fixtures/default-variable/exports.json | 62 ++
.../docgen/src/test/fixtures/markdown/code.js | 24 +
.../docgen/src/test/fixtures/markdown/docs.md | 41 ++
.../src/test/fixtures/named-class/code.js | 4 +
.../test/fixtures/named-class/exports.json | 103 +++
.../fixtures/named-default-exported/code.js | 1 +
.../named-default-exported/exports.json | 102 +++
.../named-default-exported/module-code.js | 4 +
.../named-default-exported/module-ir.json | 5 +
.../src/test/fixtures/named-default/code.js | 1 +
.../test/fixtures/named-default/exports.json | 102 +++
.../fixtures/named-default/module-code.js | 4 +
.../fixtures/named-default/module-ir.json | 5 +
.../src/test/fixtures/named-function/code.js | 4 +
.../test/fixtures/named-function/exports.json | 106 ++++
.../src/test/fixtures/named-function/ir.json | 7 +
.../named-identifier-destructuring/ast.json | 381 +++++++++++
.../named-identifier-destructuring/code.js | 6 +
.../exports.json | 82 +++
.../test/fixtures/named-identifier/ast.json | 211 ++++++
.../test/fixtures/named-identifier/code.js | 6 +
.../fixtures/named-identifier/exports.json | 82 +++
.../named-identifiers-and-inline/ast.json | 561 ++++++++++++++++
.../named-identifiers-and-inline/code.js | 17 +
.../named-identifiers-and-inline/exports.json | 290 +++++++++
.../test/fixtures/named-identifiers/ast.json | 599 ++++++++++++++++++
.../test/fixtures/named-identifiers/code.js | 16 +
.../fixtures/named-identifiers/exports.json | 200 ++++++
.../test/fixtures/named-identifiers/ir.json | 1 +
.../test/fixtures/named-import-named/code.js | 5 +
.../fixtures/named-import-named/exports.json | 220 +++++++
.../fixtures/named-import-namespace/ast.json | 186 ++++++
.../fixtures/named-import-namespace/code.js | 6 +
.../named-import-namespace/exports.json | 82 +++
.../named-import-namespace/module-code.js | 1 +
.../module-exports.json | 102 +++
.../named-import-namespace/module-ir.json | 1 +
.../src/test/fixtures/named-variable/code.js | 5 +
.../test/fixtures/named-variable/exports.json | 125 ++++
.../src/test/fixtures/named-variables/code.js | 6 +
.../fixtures/named-variables/exports.json | 185 ++++++
.../test/fixtures/namespace-commented/code.js | 4 +
.../fixtures/namespace-commented/exports.json | 62 ++
.../namespace-commented/module-ir.json | 22 +
.../fixtures/namespace-commented/module.js | 19 +
.../src/test/fixtures/namespace/code.js | 1 +
.../src/test/fixtures/namespace/exports.json | 40 ++
.../test/fixtures/namespace/module-ir.json | 22 +
.../src/test/fixtures/namespace/module.js | 19 +
.../src/test/fixtures/tags-function/code.js | 24 +
.../test/fixtures/tags-function/exports.json | 269 ++++++++
.../src/test/fixtures/tags-variable/code.js | 7 +
.../test/fixtures/tags-variable/exports.json | 125 ++++
.../docgen/src/test/formatter-markdown.js | 34 +
.../docgen/src/test/get-export-entries.js | 352 ++++++++++
.../test/get-intermediate-representation.js | 454 +++++++++++++
.../docgen/src/test/get-jsdoc-from-token.js | 78 +++
.../docgen/src/test/get-type-as-string.js | 108 ++++
packages/e2e-test-utils/README.md | 87 ++-
packages/e2e-test-utils/package.json | 6 +
112 files changed, 8511 insertions(+), 34 deletions(-)
create mode 100644 packages/docgen/.npmrc
create mode 100644 packages/docgen/CHANGELOG.md
create mode 100644 packages/docgen/README.md
create mode 100755 packages/docgen/bin/cli.js
create mode 100644 packages/docgen/coverage.md
create mode 100644 packages/docgen/package.json
create mode 100644 packages/docgen/src/engine.js
create mode 100644 packages/docgen/src/get-dependency-path.js
create mode 100644 packages/docgen/src/get-export-entries.js
create mode 100644 packages/docgen/src/get-intermediate-representation.js
create mode 100644 packages/docgen/src/get-jsdoc-from-token.js
create mode 100644 packages/docgen/src/get-leading-comments.js
create mode 100644 packages/docgen/src/get-type-as-string.js
create mode 100644 packages/docgen/src/index.js
create mode 100644 packages/docgen/src/markdown/embed.js
create mode 100644 packages/docgen/src/markdown/formatter.js
create mode 100644 packages/docgen/src/markdown/index.js
create mode 100644 packages/docgen/src/test/engine.js
create mode 100644 packages/docgen/src/test/fixtures/default-class-anonymous/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-class-anonymous/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-class-named/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-class-named/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-function-anonymous/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-function-anonymous/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-function-named/ast.json
create mode 100644 packages/docgen/src/test/fixtures/default-function-named/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-function-named/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-function-named/ir.json
create mode 100644 packages/docgen/src/test/fixtures/default-identifier/ast.json
create mode 100644 packages/docgen/src/test/fixtures/default-identifier/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-identifier/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-import-default/ast.json
create mode 100644 packages/docgen/src/test/fixtures/default-import-default/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-import-default/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-import-default/module-code.js
create mode 100644 packages/docgen/src/test/fixtures/default-import-default/module-ir.json
create mode 100644 packages/docgen/src/test/fixtures/default-import-named/ast.json
create mode 100644 packages/docgen/src/test/fixtures/default-import-named/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-import-named/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-import-named/module-code.js
create mode 100644 packages/docgen/src/test/fixtures/default-import-named/module-ir.json
create mode 100644 packages/docgen/src/test/fixtures/default-named-export/ast.json
create mode 100644 packages/docgen/src/test/fixtures/default-named-export/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-named-export/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-undocumented-nocomments/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-undocumented-nocomments/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-undocumented-oneliner/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-undocumented-oneliner/exports.json
create mode 100644 packages/docgen/src/test/fixtures/default-variable/code.js
create mode 100644 packages/docgen/src/test/fixtures/default-variable/exports.json
create mode 100644 packages/docgen/src/test/fixtures/markdown/code.js
create mode 100644 packages/docgen/src/test/fixtures/markdown/docs.md
create mode 100644 packages/docgen/src/test/fixtures/named-class/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-class/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-default-exported/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-default-exported/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-default-exported/module-code.js
create mode 100644 packages/docgen/src/test/fixtures/named-default-exported/module-ir.json
create mode 100644 packages/docgen/src/test/fixtures/named-default/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-default/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-default/module-code.js
create mode 100644 packages/docgen/src/test/fixtures/named-default/module-ir.json
create mode 100644 packages/docgen/src/test/fixtures/named-function/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-function/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-function/ir.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifier-destructuring/ast.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifier-destructuring/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-identifier-destructuring/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifier/ast.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifier/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-identifier/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifiers-and-inline/ast.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifiers-and-inline/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-identifiers-and-inline/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifiers/ast.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifiers/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-identifiers/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-identifiers/ir.json
create mode 100644 packages/docgen/src/test/fixtures/named-import-named/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-import-named/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-import-namespace/ast.json
create mode 100644 packages/docgen/src/test/fixtures/named-import-namespace/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-import-namespace/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-import-namespace/module-code.js
create mode 100644 packages/docgen/src/test/fixtures/named-import-namespace/module-exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-import-namespace/module-ir.json
create mode 100644 packages/docgen/src/test/fixtures/named-variable/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-variable/exports.json
create mode 100644 packages/docgen/src/test/fixtures/named-variables/code.js
create mode 100644 packages/docgen/src/test/fixtures/named-variables/exports.json
create mode 100644 packages/docgen/src/test/fixtures/namespace-commented/code.js
create mode 100644 packages/docgen/src/test/fixtures/namespace-commented/exports.json
create mode 100644 packages/docgen/src/test/fixtures/namespace-commented/module-ir.json
create mode 100644 packages/docgen/src/test/fixtures/namespace-commented/module.js
create mode 100644 packages/docgen/src/test/fixtures/namespace/code.js
create mode 100644 packages/docgen/src/test/fixtures/namespace/exports.json
create mode 100644 packages/docgen/src/test/fixtures/namespace/module-ir.json
create mode 100644 packages/docgen/src/test/fixtures/namespace/module.js
create mode 100644 packages/docgen/src/test/fixtures/tags-function/code.js
create mode 100644 packages/docgen/src/test/fixtures/tags-function/exports.json
create mode 100644 packages/docgen/src/test/fixtures/tags-variable/code.js
create mode 100644 packages/docgen/src/test/fixtures/tags-variable/exports.json
create mode 100644 packages/docgen/src/test/formatter-markdown.js
create mode 100644 packages/docgen/src/test/get-export-entries.js
create mode 100644 packages/docgen/src/test/get-intermediate-representation.js
create mode 100644 packages/docgen/src/test/get-jsdoc-from-token.js
create mode 100644 packages/docgen/src/test/get-type-as-string.js
diff --git a/docs/manifest.json b/docs/manifest.json
index c6eccbce1d73d..fbaa1039cf678 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -635,6 +635,12 @@
"markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/deprecated/README.md",
"parent": "packages"
},
+ {
+ "title": "@wordpress/docgen",
+ "slug": "packages-docgen",
+ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/docgen/README.md",
+ "parent": "packages"
+ },
{
"title": "@wordpress/dom-ready",
"slug": "packages-dom-ready",
diff --git a/package-lock.json b/package-lock.json
index 1b825c2ffbb3a..2f0d39cd7a38c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2701,6 +2701,17 @@
"@wordpress/hooks": "file:packages/hooks"
}
},
+ "@wordpress/docgen": {
+ "version": "file:packages/docgen",
+ "dev": true,
+ "requires": {
+ "mdast-util-inject": "1.1.0",
+ "optionator": "0.8.2",
+ "remark": "10.0.1",
+ "remark-parse": "6.0.3",
+ "unified": "7.1.0"
+ }
+ },
"@wordpress/dom": {
"version": "file:packages/dom",
"requires": {
@@ -13865,6 +13876,21 @@
"unist-util-visit": "^1.1.0"
}
},
+ "mdast-util-inject": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz",
+ "integrity": "sha1-2wa4tYW+lZotzS+H9HK6m3VvNnU=",
+ "dev": true,
+ "requires": {
+ "mdast-util-to-string": "^1.0.0"
+ }
+ },
+ "mdast-util-to-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.5.tgz",
+ "integrity": "sha512-2qLt/DEOo5F6nc2VFScQiHPzQ0XXcabquRJxKMhKte8nt42o08HUxNDPk7tt0YPxnWjAT11I1SYi0X0iPnfI5A==",
+ "dev": true
+ },
"mdn-data": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz",
diff --git a/package.json b/package.json
index eb1b308d87f2a..b0965325aa435 100644
--- a/package.json
+++ b/package.json
@@ -66,6 +66,7 @@
"@wordpress/babel-preset-default": "file:packages/babel-preset-default",
"@wordpress/browserslist-config": "file:packages/browserslist-config",
"@wordpress/custom-templated-path-webpack-plugin": "file:packages/custom-templated-path-webpack-plugin",
+ "@wordpress/docgen": "file:packages/docgen",
"@wordpress/e2e-test-utils": "file:packages/e2e-test-utils",
"@wordpress/e2e-tests": "file:packages/e2e-tests",
"@wordpress/eslint-plugin": "file:packages/eslint-plugin",
@@ -161,6 +162,7 @@
"dev": "npm run build:packages && concurrently \"wp-scripts start\" \"npm run dev:packages\"",
"dev:packages": "node ./bin/packages/watch.js",
"docs:build": "node docs/tool",
+ "docs:generate": "lerna run docs:generate",
"fixtures:clean": "rimraf \"packages/e2e-tests/fixtures/blocks/*.+(json|serialized.html)\"",
"fixtures:server-registered": "docker-compose run -w /var/www/html/wp-content/plugins/gutenberg --rm wordpress ./bin/get-server-blocks.php > test/integration/full-content/server-registered.json",
"fixtures:generate": "npm run fixtures:server-registered && cross-env GENERATE_MISSING_FIXTURES=y npm run test-unit",
diff --git a/packages/docgen/.npmrc b/packages/docgen/.npmrc
new file mode 100644
index 0000000000000..43c97e719a5a8
--- /dev/null
+++ b/packages/docgen/.npmrc
@@ -0,0 +1 @@
+package-lock=false
diff --git a/packages/docgen/CHANGELOG.md b/packages/docgen/CHANGELOG.md
new file mode 100644
index 0000000000000..ae69187866b19
--- /dev/null
+++ b/packages/docgen/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0 (Unreleased)
+
+- Initial release
diff --git a/packages/docgen/README.md b/packages/docgen/README.md
new file mode 100644
index 0000000000000..c62b96fc4e44b
--- /dev/null
+++ b/packages/docgen/README.md
@@ -0,0 +1,250 @@
+# `docgen`
+
+`docgen` helps you to generate the _public API_ of your code. Given an entry point file, it outputs the ES6 export statements and their corresponding JSDoc comments in human-readable format.
+
+Some characteristics:
+
+* If the export statement doesn't contain any JSDoc, it'll look up for JSDoc up to the declaration.
+* It can resolve relative dependencies, either files or directories. For example, `import default from './dependency'` will find `dependency.js` or `dependency/index.js`
+
+## Installation
+
+Install the module
+
+```bash
+npm install @wordpress/docgen --save-dev
+```
+
+## Usage
+
+```bash
+npx docgen
+```
+
+This command will generate a file named `entry-point-api.md` containing all the exports and their JSDoc comments.
+
+### CLI options
+
+* **--formatter** `(String)`: A path to a custom formatter to control the contents of the output file. It should be a CommonJS module that exports a function that takes as input:
+ * *rootDir* `(String)`: current working directory as seen by docgen.
+ * *docPath* `(String)`: path of the output document to generate.
+ * *symbols* `(Array)`: the symbols found.
+* **--ignore** `(RegExp)`: A regular expression used to ignore symbols whose name match it.
+* **--output** `(String)`: Output file that will contain the API documentation.
+* **--to-section** `(String)`: Append generated documentation to this section in the Markdown output. To be used by the default Markdown formatter. Depends on `--output` and bypasses the custom `--formatter` passed, if any.
+* **--to-token**: Embed generated documentation within the start and end tokens in the Markdown output. To be used by the default Markdown formatter.Depends on `--output` and bypasses the custom `--formatter` passed, if any.
+ * Start token: ``
+ * End token: ``
+* **--use-token** `(String)`: This options allows you to customize the string between the tokens. For example, `--use-token my-api` will look up for the start token `` and the end token ``. Depends on `--to-token`.
+* **--debug**: Run in debug mode, which outputs some intermediate files useful for debugging.
+
+## Examples
+
+### Default export
+
+Entry point `index.js`:
+
+```js
+/**
+ * Adds two numbers.
+ *
+ * @param {number} term1 First number.
+ * @param {number} term2 Second number.
+ * @return {number} The result of adding the two numbers.
+ */
+export default function addition( term1, term2 ) {
+ // Implementation would go here.
+}
+```
+
+Output of `npx docgen index.js` would be `index-api.js`:
+
+```markdown
+# API
+
+## default
+
+[example.js#L8-L10](example.js#L8-L10)
+
+Adds two numbers.
+
+**Parameters**
+
+- **term1** `number`: First number.
+- **term2** `number`: Second number.
+
+**Returns**
+
+`number` The result of adding the two numbers.
+```
+
+### Named export
+
+Entry point `index.js`:
+
+```js
+/**
+ * Adds two numbers.
+ *
+ * @param {number} term1 First number.
+ * @param {number} term2 Second number.
+ * @return {number} The result of adding the two numbers.
+ */
+function addition( term1, term2 ) {
+ return term1 + term2;
+}
+
+/**
+ * Adds two numbers.
+ *
+ * @deprecated Use `addition` instead.
+ *
+ * @param {number} term1 First number.
+ * @param {number} term2 Second number.
+ * @return {number} The result of adding the two numbers.
+ */
+function count( term1, term2 ) {
+ return term1 + term2;
+}
+
+export { count, addition };
+```
+
+Output of `npx docgen index.js` would be `index-api.js`:
+
+```markdown
+# API
+
+## addition
+
+[example.js#L25-L25](example.js#L25-L25)
+
+Adds two numbers.
+
+**Parameters**
+
+- **term1** `number`: First number.
+- **term2** `number`: Second number.
+
+**Returns**
+
+`number` The result of adding the two numbers.
+
+## count
+
+[example.js#L25-L25](example.js#L25-L25)
+
+> **Deprecated** Use `addition` instead.
+
+Adds two numbers.
+
+**Parameters**
+
+- **term1** `number`: First number.
+- **term2** `number`: Second number.
+
+**Returns**
+
+`number` The result of adding the two numbers.
+```
+
+### Namespace export
+
+Let the entry point be `index.js`:
+
+```js
+export * from './count';
+```
+
+with `./count/index.js` contents being:
+
+```js
+/**
+ * Substracts two numbers.
+ *
+ * @example
+ *
+ * ```js
+ * const result = substraction( 5, 2 );
+ * console.log( result ); // Will log 3
+ * ```
+ *
+ * @param {number} term1 First number.
+ * @param {number} term2 Second number.
+ * @return {number} The result of subtracting the two numbers.
+ */
+export function substraction( term1, term2 ) {
+ return term1 - term2;
+}
+
+/**
+ * Adds two numbers.
+ *
+ * @example
+ *
+ * ```js
+ * const result = addition( 5, 2 );
+ * console.log( result ); // Will log 7
+ * ```
+ *
+ * @param {number} term1 First number.
+ * @param {number} term2 Second number.
+ * @return {number} The result of adding the two numbers.
+ */
+export function addition( term1, term2 ) {
+ // Implementation would go here.
+ return term1 - term2;
+}
+```
+
+Output of `npx docgen index.js` would be `index-api.js`:
+
+````markdown
+# API
+
+## addition
+
+[example-module.js#L1-L1](example-module.js#L1-L1)
+
+Adds two numbers.
+
+**Usage**
+
+```js
+const result = addition( 5, 2 );
+console.log( result ); // Will log 7
+```
+
+**Parameters**
+
+- **term1** `number`: First number.
+- **term2** `number`: Second number.
+
+**Returns**
+
+`number` The result of adding the two numbers.
+
+## substraction
+
+[example-module.js#L1-L1](example-module.js#L1-L1)
+
+Substracts two numbers.
+
+**Usage**
+
+```js
+const result = substraction( 5, 2 );
+console.log( result ); // Will log 3
+```
+
+**Parameters**
+
+- **term1** `number`: First number.
+- **term2** `number`: Second number.
+
+**Returns**
+
+`number` The result of subtracting the two numbers.
+````
+
+
diff --git a/packages/docgen/bin/cli.js b/packages/docgen/bin/cli.js
new file mode 100755
index 0000000000000..bcc1a6f1e245d
--- /dev/null
+++ b/packages/docgen/bin/cli.js
@@ -0,0 +1,44 @@
+#!/usr/bin/env node
+
+const docgen = require( '../src' );
+
+const optionator = require( 'optionator' )( {
+ prepend: 'Usage: node ',
+ options: [ {
+ option: 'formatter',
+ type: 'String',
+ description: 'A custom function to format the generated documentation. By default, a Markdown formatter will be used.',
+ }, {
+ option: 'output',
+ type: 'String',
+ description: 'Output file to contain the API documentation.',
+ }, {
+ option: 'ignore',
+ type: 'RegExp',
+ description: 'A regular expression used to ignore symbols whose name match it.',
+ }, {
+ option: 'to-section',
+ type: 'String',
+ description: 'Append generated documentation to this section in the Markdown output. To be used by the default Markdown formatter.',
+ dependsOn: 'output',
+ }, {
+ option: 'to-token',
+ type: 'Boolean',
+ description: 'Embed generated documentation within this token in the Markdown output. To be used by the default Markdown formatter.',
+ dependsOn: 'output',
+ }, {
+ option: 'use-token',
+ type: 'String',
+ default: 'Autogenerated API docs',
+ description: 'Add this string to the start/end tokens.',
+ dependsOn: 'to-token',
+ }, {
+ option: 'debug',
+ type: 'Boolean',
+ default: false,
+ description: 'Run in debug mode, which outputs some intermediate files useful for debugging.',
+ } ],
+} );
+
+const options = optionator.parseArgv( process.argv );
+docgen( options._[ 0 ], options );
diff --git a/packages/docgen/coverage.md b/packages/docgen/coverage.md
new file mode 100644
index 0000000000000..83c5dfd3d59fa
--- /dev/null
+++ b/packages/docgen/coverage.md
@@ -0,0 +1,83 @@
+# Coverage
+
+## Packages outside of scope
+
+- babel-plugin-makepot. CommonJS module. Babel plugin.
+- babel-preset-default. CommonJS module. Babel preset.
+- browserslist-config. CommonJS module. Config.
+- custom-templated-path-webpack-plugin. CommonJS module. Webpack plugin.
+- docgen. CommonJS module.
+- e2e-tests. Do not export anything.
+- eslint-plugin. CommonJS module. ESLint plugin.
+- is-shallow-equal. CommonJS module.
+- jest-preset-default. CommonJS module. Jest preset.
+- library-export-default-webpack-plugin. CommonJS. Webpack plugin.
+- npm-package-json-lint-config. CommonJS. Config.
+- postcss-themes. CommonJS module.
+- scripts. CommonJS module.
+
+## TODO
+
+These either happen in private API, aren't relevant, or are pending of decission.
+
+- [ ] go undocummented: `unstable__*`, rich-text `unstableToDom`, `experimental__`
+- [ ] `constants` keycodes, rich-text `LINE_SEPARATOR`
+- [ ] `{?{ time: number, count: number }}`packages/editor/src/store/selectors.js
+- [ ] `{type=}` packages/block-library/src/image/edit.js
+- [ ] `@api` packages/editor/src/editor-styles/ast/stringify/compiler.js
+- [ ] `@callback` packages/components/src/autocomplete/index.js
+- [ ] `@cite` packages/block-serialization-default-parser/src/index.js
+- [ ] `@class` packages/edit-post/src/hooks/components/media-upload/index.js
+- [ ] `@const` packages/editor/src/editor-styles/transforms/wrap.js
+- [ ] `@constant` packages/editor/src/hooks/align.js
+- [ ] `@constructor` packages/edit-post/src/hooks/components/media-upload/index.js
+- [ ] `@inheritdoc` packages/edit-post/src/components/meta-boxes/meta-boxes-area/index.js
+- [ ] `@private` packages/editor/src/components/rich-text/index.js
+- [ ] `@property` babel-plugin-import-jsx-pragma
+- [ ] `@since` packages/block-serialization-default-parser/src/index.js
+- [ ] `@throws` packages/blocks/src/api/node.js
+- [ ] `@typedef` packages/blocks/src/api/registration.js
+
+## DONE
+
+- [x] a11y
+- [x] annotations
+- [x] api-fetch
+- [x] babel-plugin-import-jsx-pragma
+- [x] blob
+- [x] block-library
+- [x] block-serialization-default-parser
+- [x] block-serialization-spec-parser
+- [x] blocks
+- [x] components
+- [x] compose
+- [x] core-data
+- [x] data
+- [x] date
+- [x] deprecated
+- [x] dom
+- [x] dom-ready
+- [x] e2e-test-utils
+- [x] edit-post
+- [x] editor
+- [x] element
+- [x] escape-html
+- [x] format-library
+- [x] hooks
+- [x] html-entities
+- [x] i18n
+- [x] jest-console
+- [x] jest-puppeteer-axe
+- [x] keycodes
+- [x] list-reusable-blocks
+- [x] notices
+- [x] nux
+- [x] plugins
+- [x] priority-queue
+- [x] redux-routine
+- [x] rich-text
+- [x] shortcode
+- [x] token-list
+- [x] url
+- [x] viewport
+- [x] wordcount
diff --git a/packages/docgen/package.json b/packages/docgen/package.json
new file mode 100644
index 0000000000000..a73ed098f3142
--- /dev/null
+++ b/packages/docgen/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@wordpress/docgen",
+ "version": "1.0.0-beta.0",
+ "description": "Autogenerate public API documentation from exports and JSDoc comments.",
+ "author": "The WordPress Contributors",
+ "license": "GPL-2.0-or-later",
+ "keywords": [
+ "jsdoc",
+ "documentation",
+ "wordpress"
+ ],
+ "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/docgen/README.md",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/WordPress/gutenberg.git"
+ },
+ "bugs": {
+ "url": "https://github.com/WordPress/gutenberg/issues"
+ },
+ "bin": {
+ "docgen": "./bin/cli.js"
+ },
+ "dependencies": {
+ "mdast-util-inject": "1.1.0",
+ "optionator": "0.8.2",
+ "remark": "10.0.1",
+ "remark-parse": "6.0.3",
+ "unified": "7.1.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/docgen/src/engine.js b/packages/docgen/src/engine.js
new file mode 100644
index 0000000000000..0c01f94ef9b50
--- /dev/null
+++ b/packages/docgen/src/engine.js
@@ -0,0 +1,56 @@
+/**
+* External dependencies.
+*/
+const espree = require( 'espree' );
+const { flatten } = require( 'lodash' );
+
+/**
+* Internal dependencies.
+*/
+const getIntermediateRepresentation = require( './get-intermediate-representation' );
+
+const getAST = ( source ) => espree.parse( source, {
+ attachComment: true,
+ loc: true,
+ ecmaVersion: 2018,
+ ecmaFeatures: {
+ jsx: true,
+ },
+ sourceType: 'module',
+} );
+
+const getExportTokens = ( ast ) => ast.body.filter(
+ ( node ) => [
+ 'ExportNamedDeclaration',
+ 'ExportDefaultDeclaration',
+ 'ExportAllDeclaration',
+ ].some( ( declaration ) => declaration === node.type )
+);
+
+const engine = ( path, code, getIRFromPath = () => {} ) => {
+ const result = {};
+ result.ast = getAST( code );
+ result.tokens = getExportTokens( result.ast );
+ result.ir = flatten( result.tokens.map(
+ ( token ) => getIntermediateRepresentation(
+ path,
+ token,
+ result.ast,
+ getIRFromPath
+ )
+ ) );
+
+ return result;
+};
+
+/**
+ * Function that takes code and returns an intermediate representation.
+ *
+ * @param {string} code The code to parse.
+ * @param {Function} [getIRFromPath=noop] Callback to retrieve the
+ * Intermediate Representation from a path relative to the file
+ * being parsed.
+ *
+ * @return {Object} Intermediate Representation in JSON.
+ */
+module.exports = engine;
diff --git a/packages/docgen/src/get-dependency-path.js b/packages/docgen/src/get-dependency-path.js
new file mode 100644
index 0000000000000..83c043023ed44
--- /dev/null
+++ b/packages/docgen/src/get-dependency-path.js
@@ -0,0 +1,3 @@
+module.exports = function( token ) {
+ return token.source.value;
+};
diff --git a/packages/docgen/src/get-export-entries.js b/packages/docgen/src/get-export-entries.js
new file mode 100644
index 0000000000000..a69811b2b6545
--- /dev/null
+++ b/packages/docgen/src/get-export-entries.js
@@ -0,0 +1,97 @@
+/**
+ * External dependencies
+ */
+const { get } = require( 'lodash' );
+
+/**
+ * Returns the export entry records of the given export statement.
+ * Unlike [the standard](http://www.ecma-international.org/ecma-262/9.0/#exportentry-record),
+ * the `importName` and the `localName` are merged together.
+ *
+ * @param {Object} token Espree node representing an export.
+ *
+ * @return {Array} Exported entry records. Example:
+ * [ {
+ * localName: 'localName',
+ * exportName: 'exportedName',
+ * module: null,
+ * lineStart: 2,
+ * lineEnd: 3,
+ * } ]
+ */
+module.exports = function( token ) {
+ if ( token.type === 'ExportDefaultDeclaration' ) {
+ const getLocalName = ( t ) => {
+ let name;
+ switch ( t.declaration.type ) {
+ case 'Identifier':
+ name = t.declaration.name;
+ break;
+ case 'AssignmentExpression':
+ name = t.declaration.left.name;
+ break;
+ //case 'FunctionDeclaration'
+ //case 'ClassDeclaration'
+ default:
+ name = get( t.declaration, [ 'id', 'name' ], '*default*' );
+ }
+ return name;
+ };
+ return [ {
+ localName: getLocalName( token ),
+ exportName: 'default',
+ module: null,
+ lineStart: token.loc.start.line,
+ lineEnd: token.loc.end.line,
+ } ];
+ }
+
+ if ( token.type === 'ExportAllDeclaration' ) {
+ return [ {
+ localName: '*',
+ exportName: null,
+ module: token.source.value,
+ lineStart: token.loc.start.line,
+ lineEnd: token.loc.end.line,
+ } ];
+ }
+
+ const name = [];
+ if ( token.declaration === null ) {
+ token.specifiers.forEach( ( specifier ) => name.push( {
+ localName: specifier.local.name,
+ exportName: specifier.exported.name,
+ module: get( token.source, [ 'value' ], null ),
+ lineStart: specifier.loc.start.line,
+ lineEnd: specifier.loc.end.line,
+ } ) );
+ return name;
+ }
+
+ switch ( token.declaration.type ) {
+ case 'ClassDeclaration':
+ case 'FunctionDeclaration':
+ name.push( {
+ localName: token.declaration.id.name,
+ exportName: token.declaration.id.name,
+ module: null,
+ lineStart: token.declaration.loc.start.line,
+ lineEnd: token.declaration.loc.end.line,
+ } );
+ break;
+
+ case 'VariableDeclaration':
+ token.declaration.declarations.forEach( ( declaration ) => {
+ name.push( {
+ localName: declaration.id.name,
+ exportName: declaration.id.name,
+ module: null,
+ lineStart: token.declaration.loc.start.line,
+ lineEnd: token.declaration.loc.end.line,
+ } );
+ } );
+ break;
+ }
+
+ return name;
+};
diff --git a/packages/docgen/src/get-intermediate-representation.js b/packages/docgen/src/get-intermediate-representation.js
new file mode 100644
index 0000000000000..eee7775d69979
--- /dev/null
+++ b/packages/docgen/src/get-intermediate-representation.js
@@ -0,0 +1,152 @@
+/**
+ * External dependencies.
+ */
+const { get } = require( 'lodash' );
+
+/**
+ * Internal dependencies.
+ */
+const getExportEntries = require( './get-export-entries' );
+const getJSDocFromToken = require( './get-jsdoc-from-token' );
+const getDependencyPath = require( './get-dependency-path' );
+
+const UNDOCUMENTED = 'Undocumented declaration.';
+const NAMESPACE_EXPORT = '*';
+const DEFAULT_EXPORT = 'default';
+
+const hasClassWithName = ( node, name ) =>
+ node.type === 'ClassDeclaration' &&
+ node.id.name === name;
+
+const hasFunctionWithName = ( node, name ) =>
+ node.type === 'FunctionDeclaration' &&
+ node.id.name === name;
+
+const hasVariableWithName = ( node, name ) =>
+ node.type === 'VariableDeclaration' &&
+ node.declarations.some( ( declaration ) => {
+ if ( declaration.id.type === 'ObjectPattern' ) {
+ return declaration.id.properties.some(
+ ( property ) => property.key.name === name
+ );
+ }
+ return declaration.id.name === name;
+ } );
+
+const hasNamedExportWithName = ( node, name ) =>
+ node.type === 'ExportNamedDeclaration' && (
+ ( node.declaration && hasClassWithName( node.declaration, name ) ) ||
+ ( node.declaration && hasFunctionWithName( node.declaration, name ) ) ||
+ ( node.declaration && hasVariableWithName( node.declaration, name ) )
+ );
+
+const hasImportWithName = ( node, name ) =>
+ node.type === 'ImportDeclaration' &&
+ node.specifiers.some( ( specifier ) => specifier.local.name === name );
+
+const isImportDeclaration = ( node ) => node.type === 'ImportDeclaration';
+
+const someImportMatchesName = ( name, token ) => {
+ let matches = false;
+ token.specifiers.forEach( ( specifier ) => {
+ if ( ( specifier.type === 'ImportDefaultSpecifier' ) && ( name === 'default' ) ) {
+ matches = true;
+ }
+ if ( ( specifier.type === 'ImportSpecifier' ) && ( name === specifier.imported.name ) ) {
+ matches = true;
+ }
+ } );
+ return matches;
+};
+
+const someEntryMatchesName = ( name, entry, token ) =>
+ ( token.type === 'ExportNamedDeclaration' && entry.localName === name ) ||
+ ( token.type === 'ImportDeclaration' && someImportMatchesName( name, token ) );
+
+const getJSDocFromDependency = ( token, entry, parseDependency ) => {
+ let doc;
+ const ir = parseDependency( getDependencyPath( token ) );
+ if ( entry.localName === NAMESPACE_EXPORT ) {
+ doc = ir.filter( ( { name } ) => name !== DEFAULT_EXPORT );
+ } else {
+ doc = ir.find( ( { name } ) => someEntryMatchesName( name, entry, token ) );
+ }
+ return doc;
+};
+
+const getJSDoc = ( token, entry, ast, parseDependency ) => {
+ let doc;
+ if ( entry.localName !== NAMESPACE_EXPORT ) {
+ doc = getJSDocFromToken( token );
+ if ( ( doc !== undefined ) ) {
+ return doc;
+ }
+ }
+
+ if ( entry && entry.module === null ) {
+ const candidates = ast.body.filter( ( node ) => {
+ return hasClassWithName( node, entry.localName ) ||
+ hasFunctionWithName( node, entry.localName ) ||
+ hasVariableWithName( node, entry.localName ) ||
+ hasNamedExportWithName( node, entry.localName ) ||
+ hasImportWithName( node, entry.localName );
+ } );
+ if ( candidates.length !== 1 ) {
+ return doc;
+ }
+ const node = candidates[ 0 ];
+ if ( isImportDeclaration( node ) ) {
+ doc = getJSDocFromDependency( node, entry, parseDependency );
+ } else {
+ doc = getJSDocFromToken( node );
+ }
+ return doc;
+ }
+
+ return getJSDocFromDependency( token, entry, parseDependency );
+};
+
+/**
+ * Takes a export token and returns an intermediate representation in JSON.
+ *
+ * If the export token doesn't contain any JSDoc, and it's a identifier,
+ * the identifier declaration will be looked up in the file or dependency
+ * if an `ast` and `parseDependency` callback are provided.
+ *
+ * @param {string} path Path to file being processed.
+ * @param {Object} token Espree export token.
+ * @param {Object} [ast] Espree ast of the file being parsed.
+ * @param {Function} [parseDependency] Function that takes a path
+ * and returns the intermediate representation of the dependency file.
+ *
+ * @return {Object} Intermediate Representation in JSON.
+ */
+module.exports = function( path, token, ast = { body: [] }, parseDependency = () => {} ) {
+ const exportEntries = getExportEntries( token );
+ const ir = [];
+ exportEntries.forEach( ( entry ) => {
+ const doc = getJSDoc( token, entry, ast, parseDependency );
+ if ( entry.localName === NAMESPACE_EXPORT ) {
+ doc.forEach( ( namedExport ) => {
+ ir.push( {
+ path,
+ name: namedExport.name,
+ description: namedExport.description,
+ tags: namedExport.tags,
+ lineStart: entry.lineStart,
+ lineEnd: entry.lineEnd,
+ } );
+ } );
+ } else {
+ ir.push( {
+ path,
+ name: entry.exportName,
+ description: get( doc, [ 'description' ], UNDOCUMENTED ),
+ tags: get( doc, [ 'tags' ], [] ),
+ lineStart: entry.lineStart,
+ lineEnd: entry.lineEnd,
+ } );
+ }
+ } );
+ return ir;
+};
diff --git a/packages/docgen/src/get-jsdoc-from-token.js b/packages/docgen/src/get-jsdoc-from-token.js
new file mode 100644
index 0000000000000..e888abc53d06c
--- /dev/null
+++ b/packages/docgen/src/get-jsdoc-from-token.js
@@ -0,0 +1,37 @@
+/**
+ * External dependencies.
+ */
+const doctrine = require( 'doctrine' );
+
+/**
+ * Internal dependencies.
+ */
+const getLeadingComments = require( './get-leading-comments' );
+const getTypeAsString = require( './get-type-as-string' );
+
+/**
+ * Function that takes an Espree token and returns
+ * a object representing the leading JSDoc comment of the token,
+ * if any.
+ *
+ * @param {Object} token Espree token.
+ * @return {Object} Object representing the JSDoc comment.
+ */
+module.exports = function( token ) {
+ let jsdoc;
+ const comments = getLeadingComments( token );
+ if ( comments && comments.startsWith( '*\n' ) ) {
+ jsdoc = doctrine.parse( comments, {
+ unwrap: true,
+ recoverable: true,
+ sloppy: true,
+ } );
+ jsdoc.tags = jsdoc.tags.map( ( tag ) => {
+ if ( tag.type ) {
+ tag.type = getTypeAsString( tag.type );
+ }
+ return tag;
+ } );
+ }
+ return jsdoc;
+};
diff --git a/packages/docgen/src/get-leading-comments.js b/packages/docgen/src/get-leading-comments.js
new file mode 100644
index 0000000000000..3f025278b1033
--- /dev/null
+++ b/packages/docgen/src/get-leading-comments.js
@@ -0,0 +1,20 @@
+/**
+ * External dependencies.
+ */
+const { last } = require( 'lodash' );
+
+/**
+ * Function that returns the leading comment
+ * of a Espree node.
+ *
+ * @param {Object} declaration Espree node to inspect
+ *
+ * @return {?string} Leading comment or undefined if there is none.
+ */
+module.exports = function( declaration ) {
+ let comments;
+ if ( declaration.leadingComments ) {
+ comments = last( declaration.leadingComments ).value;
+ }
+ return comments;
+};
diff --git a/packages/docgen/src/get-type-as-string.js b/packages/docgen/src/get-type-as-string.js
new file mode 100644
index 0000000000000..64bfaefd00d52
--- /dev/null
+++ b/packages/docgen/src/get-type-as-string.js
@@ -0,0 +1,44 @@
+const maybeAddDefault = function( value, defaultValue ) {
+ if ( defaultValue ) {
+ return `value=${ defaultValue }`;
+ }
+ return value;
+};
+
+const getType = function( param, defaultValue ) {
+ if ( ! defaultValue ) {
+ defaultValue = param.default;
+ }
+
+ if ( param.type.type ) {
+ return getType( param.type, defaultValue );
+ } else if ( param.expression ) {
+ if ( param.type === 'RestType' ) {
+ return `...${ getType( param.expression, defaultValue ) }`;
+ } else if ( param.type === 'NullableType' ) {
+ return `?${ getType( param.expression, defaultValue ) }`;
+ } else if ( param.type === 'TypeApplication' ) {
+ return `${ getType( param.expression, defaultValue ) }<${
+ param.applications.map( ( application ) => getType( application ) ).join( ',' )
+ }>`;
+ } else if ( param.type === 'OptionalType' ) {
+ return `[${ getType( param.expression, defaultValue ) }]`;
+ }
+ return getType( param.expression, defaultValue );
+ } else if ( param.elements ) {
+ const types = param.elements.map( ( element ) => getType( element ) );
+ return maybeAddDefault( `(${ types.join( '|' ) })`, defaultValue );
+ } else if ( param.type === 'AllLiteral' ) {
+ return maybeAddDefault( '*', defaultValue );
+ } else if ( param.type === 'NullLiteral' ) {
+ return maybeAddDefault( 'null', defaultValue );
+ } else if ( param.type === 'UndefinedLiteral' ) {
+ return maybeAddDefault( 'undefined', defaultValue );
+ }
+
+ return maybeAddDefault( param.name, defaultValue );
+};
+
+module.exports = function( param ) {
+ return getType( param );
+};
diff --git a/packages/docgen/src/index.js b/packages/docgen/src/index.js
new file mode 100644
index 0000000000000..1bc2e71a37897
--- /dev/null
+++ b/packages/docgen/src/index.js
@@ -0,0 +1,126 @@
+/**
+ * External dependencies
+ */
+const fs = require( 'fs' );
+const path = require( 'path' );
+const { last } = require( 'lodash' );
+
+/**
+ * Internal dependencies
+ */
+const engine = require( './engine' );
+const defaultMarkdownFormatter = require( './markdown' );
+
+/**
+ * Helpers functions.
+ */
+
+const relativeToAbsolute = ( basePath, relativePath ) => {
+ const target = path.join( path.dirname( basePath ), relativePath );
+ if ( path.extname( target ) === '.js' ) {
+ return target;
+ }
+ let targetFile = target + '.js';
+ if ( fs.existsSync( targetFile ) ) {
+ return targetFile;
+ }
+ targetFile = path.join( target, 'index.js' );
+ if ( fs.existsSync( targetFile ) ) {
+ return targetFile;
+ }
+ process.stdout.write( '\nRelative path does not exists.' );
+ process.stdout.write( '\n' );
+ process.stdout.write( `\nBase: ${ basePath }` );
+ process.stdout.write( `\nRelative: ${ relativePath }` );
+ process.stdout.write( '\n\n' );
+ process.exit( 1 );
+};
+
+const getIRFromRelativePath = ( rootDir, basePath ) => ( relativePath ) => {
+ if ( ! relativePath.startsWith( '.' ) ) {
+ return [];
+ }
+ const absolutePath = relativeToAbsolute( basePath, relativePath );
+ const result = processFile( rootDir, absolutePath );
+ return result.ir || undefined;
+};
+
+const processFile = ( rootDir, inputFile ) => {
+ try {
+ const data = fs.readFileSync( inputFile, 'utf8' );
+ currentFileStack.push( inputFile );
+ const relativePath = path.relative( rootDir, inputFile );
+ const result = engine( relativePath, data, getIRFromRelativePath( rootDir, last( currentFileStack ) ) );
+ currentFileStack.pop( inputFile );
+ return result;
+ } catch ( e ) {
+ process.stdout.write( `\n${ e }` );
+ process.stdout.write( '\n\n' );
+ process.exit( 1 );
+ }
+};
+
+const runCustomFormatter = ( customFormatterFile, rootDir, doc, symbols, headingTitle ) => {
+ try {
+ const customFormatter = require( customFormatterFile );
+ const output = customFormatter( rootDir, doc, symbols, headingTitle );
+ fs.writeFileSync( doc, output );
+ } catch ( e ) {
+ process.stdout.write( `\n${ e }` );
+ process.stdout.write( '\n\n' );
+ process.exit( 1 );
+ }
+ return 'custom formatter';
+};
+
+// To keep track of file being processed.
+const currentFileStack = [];
+
+module.exports = function( sourceFile, options ) {
+ // Input: process CLI args, prepare files, etc
+ const processDir = process.cwd();
+ if ( sourceFile === undefined ) {
+ process.stdout.write( '\n' );
+ process.stdout.write( 'No source file provided' );
+ process.stdout.write( '\n\n' );
+ process.exit( 1 );
+ }
+ sourceFile = path.join( processDir, sourceFile );
+
+ const debugMode = options.debug ? true : false;
+
+ const inputBase = path.join(
+ path.dirname( sourceFile ),
+ path.basename( sourceFile, path.extname( sourceFile ) )
+ );
+ const ast = inputBase + '-ast.json';
+ const tokens = inputBase + '-exports.json';
+ const ir = inputBase + '-ir.json';
+ const doc = options.output ?
+ path.join( processDir, options.output ) :
+ inputBase + '-api.md';
+
+ // Process
+ const result = processFile( processDir, sourceFile );
+ const filteredIr = result.ir.filter( ( { name } ) => options.ignore ? ! name.match( options.ignore ) : true );
+
+ // Ouput
+ if ( result === undefined ) {
+ process.stdout.write( '\nFile was processed, but contained no ES6 module exports:' );
+ process.stdout.write( `\n${ sourceFile }` );
+ process.stdout.write( '\n\n' );
+ process.exit( 0 );
+ }
+
+ if ( options.formatter ) {
+ runCustomFormatter( path.join( processDir, options.formatter ), processDir, doc, filteredIr, 'API' );
+ } else {
+ defaultMarkdownFormatter( options, processDir, doc, filteredIr, 'API' );
+ }
+
+ if ( debugMode ) {
+ fs.writeFileSync( ir, JSON.stringify( result.ir ) );
+ fs.writeFileSync( tokens, JSON.stringify( result.tokens ) );
+ fs.writeFileSync( ast, JSON.stringify( result.ast ) );
+ }
+};
diff --git a/packages/docgen/src/markdown/embed.js b/packages/docgen/src/markdown/embed.js
new file mode 100644
index 0000000000000..9b52064c3377a
--- /dev/null
+++ b/packages/docgen/src/markdown/embed.js
@@ -0,0 +1,51 @@
+/**
+ * External dependencies
+ */
+const { findLast } = require( 'lodash' );
+
+const getHeadingIndex = ( ast, index ) => {
+ const astBeforeIndex = ast.children.slice( 0, index );
+ const lastHeading = findLast( astBeforeIndex, ( node ) => node.type === 'heading' );
+ return lastHeading ? lastHeading.depth : 1;
+};
+
+/**
+ * Inserts new contents within the token boundaries.
+ *
+ * @param {string} token String to embed in the start/end tokens.
+ * @param {Object} targetAst The remark AST of the file where the new contents are to be embedded.
+ * @param {Object} newContentAst The new contents to be embedded in remark AST format.
+ * @return {boolean} Whether the contents were embedded or not.
+ */
+const embed = function( token, targetAst, newContentAst ) {
+ let headingIndex = -1;
+
+ const START_TOKEN = ``;
+ const END_TOKEN = ``;
+ const startIndex = targetAst.children.findIndex(
+ ( node ) => node.type === 'html' && node.value === START_TOKEN
+ );
+ if ( startIndex === -1 ) {
+ return false;
+ }
+ const endIndex = targetAst.children.findIndex(
+ ( node ) => node.type === 'html' && node.value === END_TOKEN
+ );
+ if ( endIndex === -1 ) {
+ return false;
+ }
+
+ if ( startIndex !== -1 && endIndex !== -1 && startIndex < endIndex ) {
+ headingIndex = getHeadingIndex( targetAst, startIndex );
+ newContentAst.children.forEach( ( node ) => {
+ if ( node.type === 'heading' ) {
+ node.depth = headingIndex + 1;
+ }
+ } );
+ targetAst.children.splice( startIndex + 1, endIndex - startIndex - 1, newContentAst );
+ return true;
+ }
+ return false;
+};
+
+module.exports = embed;
diff --git a/packages/docgen/src/markdown/formatter.js b/packages/docgen/src/markdown/formatter.js
new file mode 100644
index 0000000000000..d9e41536f2f9a
--- /dev/null
+++ b/packages/docgen/src/markdown/formatter.js
@@ -0,0 +1,128 @@
+/**
+ * External dependencies
+ */
+const path = require( 'path' );
+
+const getTagsByName = ( tags, ...names ) => tags.filter( ( tag ) => names.some( ( name ) => name === tag.title ) );
+
+const cleanSpaces = ( paragraph ) =>
+ paragraph ?
+ paragraph.split( '\n' ).map(
+ ( sentence ) => sentence.trim()
+ ).reduce(
+ ( acc, current ) => acc + ' ' + current,
+ ''
+ ).trim() :
+ '';
+
+const formatTag = ( title, tags, formatter, docs ) => {
+ if ( tags && tags.length > 0 ) {
+ docs.push( '\n' );
+ docs.push( '\n' );
+ docs.push( `**${ title }**` );
+ docs.push( '\n' );
+ docs.push( ...tags.map( formatter ) );
+ }
+};
+
+const formatExamples = ( tags, docs ) => {
+ if ( tags && tags.length > 0 ) {
+ docs.push( '\n' );
+ docs.push( '\n' );
+ docs.push( '**Usage**' );
+ docs.push( '\n' );
+ docs.push( '\n' );
+ docs.push( ...tags.map(
+ ( tag ) => `${ tag.description }`
+ ).join( '\n\n' ) );
+ }
+};
+
+const formatDeprecated = ( tags, docs ) => {
+ if ( tags && tags.length > 0 ) {
+ docs.push( '\n' );
+ docs.push( ...tags.map(
+ ( tag ) => `\n> **Deprecated** ${ cleanSpaces( tag.description ) }`
+ ) );
+ }
+};
+
+const formatDescription = ( description, docs ) => {
+ docs.push( '\n' );
+ docs.push( '\n' );
+ docs.push( description );
+};
+
+const getHeading = ( index, text ) => {
+ return '#'.repeat( index ) + ' ' + text;
+};
+
+module.exports = function( rootDir, docPath, symbols, headingTitle, headingStartIndex ) {
+ const docs = [ ];
+ let headingIndex = headingStartIndex || 1;
+ if ( headingTitle ) {
+ docs.push( getHeading( headingIndex, `${ headingTitle }` ) );
+ headingIndex++;
+ }
+ docs.push( '\n' );
+ docs.push( '\n' );
+ symbols.sort( ( first, second ) => {
+ const firstName = first.name.toUpperCase();
+ const secondName = second.name.toUpperCase();
+ if ( firstName < secondName ) {
+ return -1;
+ }
+ if ( firstName > secondName ) {
+ return 1;
+ }
+ return 0;
+ } );
+ if ( symbols && symbols.length > 0 ) {
+ symbols.forEach( ( symbol ) => {
+ const symbolPath = path.join(
+ path.relative(
+ path.dirname( docPath ),
+ path.join( rootDir, path.dirname( symbol.path ) )
+ ),
+ path.basename( symbol.path ),
+ );
+ const symbolPathWithLines = `${ symbolPath }#L${ symbol.lineStart }-L${ symbol.lineEnd }`;
+ docs.push( getHeading( headingIndex, `${ symbol.name }` ) );
+ docs.push( `\n\n[${ symbolPathWithLines }](${ symbolPathWithLines })` );
+ formatDeprecated( getTagsByName( symbol.tags, 'deprecated' ), docs );
+ formatDescription( symbol.description, docs );
+ formatTag(
+ 'Related',
+ getTagsByName( symbol.tags, 'see', 'link' ),
+ ( tag ) => `\n- ${ tag.description }`,
+ docs
+ );
+ formatExamples( getTagsByName( symbol.tags, 'example' ), docs );
+ formatTag(
+ 'Type',
+ getTagsByName( symbol.tags, 'type' ),
+ ( tag ) => `\n\`${ tag.type }\` ${ cleanSpaces( tag.description ) }`,
+ docs
+ );
+ formatTag(
+ 'Parameters',
+ getTagsByName( symbol.tags, 'param' ),
+ ( tag ) => `\n- **${ tag.name }** \`${ tag.type }\`: ${ cleanSpaces( tag.description ) }`,
+ docs
+ );
+ formatTag(
+ 'Returns',
+ getTagsByName( symbol.tags, 'return' ),
+ ( tag ) => `\n\`${ tag.type }\` ${ cleanSpaces( tag.description ) }`,
+ docs
+ );
+ docs.push( '\n' );
+ docs.push( '\n' );
+ } );
+ docs.pop(); // remove last \n, we want one blank line at the end of the file.
+ } else {
+ docs.push( 'Nothing to document.' );
+ docs.push( '\n' );
+ }
+ return docs.join( '' );
+};
diff --git a/packages/docgen/src/markdown/index.js b/packages/docgen/src/markdown/index.js
new file mode 100644
index 0000000000000..499e211e1873a
--- /dev/null
+++ b/packages/docgen/src/markdown/index.js
@@ -0,0 +1,44 @@
+/**
+ * External dependencies.
+ */
+const remark = require( 'remark' );
+const unified = require( 'unified' );
+const remarkParser = require( 'remark-parse' );
+const inject = require( 'mdast-util-inject' );
+const fs = require( 'fs' );
+
+/**
+ * Internal dependencies.
+ */
+const formatter = require( './formatter' );
+const embed = require( './embed' );
+
+const appendOrEmbedContents = ( { options, newContents } ) => {
+ return function transform( targetAst, file, next ) {
+ if ( options.toSection && ! inject( options.toSection, targetAst, newContents ) ) {
+ return next( new Error( `Heading ${ options.toSection } not found.` ) );
+ } else if ( options.toToken && ! embed( options.useToken, targetAst, newContents ) ) {
+ return next( new Error( `Start and/or end tokens for ${ options.useToken } not found.` ) );
+ }
+ next();
+ };
+};
+
+module.exports = function( options, processDir, doc, filteredIr, headingTitle ) {
+ if ( options.toSection || options.toToken ) {
+ const currentReadmeFile = fs.readFileSync( options.output, 'utf8' );
+ const newContents = unified().use( remarkParser ).parse( formatter( processDir, doc, filteredIr, null ) );
+ remark()
+ .use( { settings: { commonmark: true } } )
+ .use( appendOrEmbedContents, { options, newContents } )
+ .process( currentReadmeFile, function( err, file ) {
+ if ( err ) {
+ throw err;
+ }
+ fs.writeFileSync( doc, file );
+ } );
+ } else {
+ const output = formatter( processDir, doc, filteredIr, headingTitle );
+ fs.writeFileSync( doc, output );
+ }
+};
diff --git a/packages/docgen/src/test/engine.js b/packages/docgen/src/test/engine.js
new file mode 100644
index 0000000000000..3a9efc921a2f0
--- /dev/null
+++ b/packages/docgen/src/test/engine.js
@@ -0,0 +1,11 @@
+/**
+ * Internal dependencies.
+ */
+const engine = require( '../engine' );
+
+describe( 'Engine', () => {
+ it( 'should return a void IR for undefined code', () => {
+ const { ir } = engine( undefined );
+ expect( ir ).toHaveLength( 0 );
+ } );
+} );
diff --git a/packages/docgen/src/test/fixtures/default-class-anonymous/code.js b/packages/docgen/src/test/fixtures/default-class-anonymous/code.js
new file mode 100644
index 0000000000000..79ca0d6a39fcd
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-class-anonymous/code.js
@@ -0,0 +1,4 @@
+/**
+ * Class declaration example.
+ */
+export default class {}
diff --git a/packages/docgen/src/test/fixtures/default-class-anonymous/exports.json b/packages/docgen/src/test/fixtures/default-class-anonymous/exports.json
new file mode 100644
index 0000000000000..858f07abe98df
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-class-anonymous/exports.json
@@ -0,0 +1,82 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 38,
+ "end": 61,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 23
+ }
+ },
+ "range": [
+ 38,
+ 61
+ ],
+ "declaration": {
+ "type": "ClassDeclaration",
+ "start": 53,
+ "end": 61,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 15
+ },
+ "end": {
+ "line": 4,
+ "column": 23
+ }
+ },
+ "range": [
+ 53,
+ 61
+ ],
+ "id": null,
+ "superClass": null,
+ "body": {
+ "type": "ClassBody",
+ "start": 59,
+ "end": 61,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 21
+ },
+ "end": {
+ "line": 4,
+ "column": 23
+ }
+ },
+ "range": [
+ 59,
+ 61
+ ],
+ "body": []
+ }
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 0,
+ "end": 37,
+ "range": [
+ 0,
+ 37
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-class-named/code.js b/packages/docgen/src/test/fixtures/default-class-named/code.js
new file mode 100644
index 0000000000000..91649c8da44b5
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-class-named/code.js
@@ -0,0 +1,4 @@
+/**
+ * Class declaration example.
+ */
+export default class ClassDeclaration {}
diff --git a/packages/docgen/src/test/fixtures/default-class-named/exports.json b/packages/docgen/src/test/fixtures/default-class-named/exports.json
new file mode 100644
index 0000000000000..7533ef32e60de
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-class-named/exports.json
@@ -0,0 +1,101 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 38,
+ "end": 78,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 38,
+ 78
+ ],
+ "declaration": {
+ "type": "ClassDeclaration",
+ "start": 53,
+ "end": 78,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 15
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 53,
+ 78
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 59,
+ "end": 75,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 21
+ },
+ "end": {
+ "line": 4,
+ "column": 37
+ }
+ },
+ "range": [
+ 59,
+ 75
+ ],
+ "name": "ClassDeclaration"
+ },
+ "superClass": null,
+ "body": {
+ "type": "ClassBody",
+ "start": 76,
+ "end": 78,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 38
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 76,
+ 78
+ ],
+ "body": []
+ }
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 0,
+ "end": 37,
+ "range": [
+ 0,
+ 37
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-function-anonymous/code.js b/packages/docgen/src/test/fixtures/default-function-anonymous/code.js
new file mode 100644
index 0000000000000..5c8c7e23a946c
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-function-anonymous/code.js
@@ -0,0 +1,4 @@
+/**
+ * Function declaration example.
+ */
+export default function() {}
diff --git a/packages/docgen/src/test/fixtures/default-function-anonymous/exports.json b/packages/docgen/src/test/fixtures/default-function-anonymous/exports.json
new file mode 100644
index 0000000000000..9d10cd6c962f0
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-function-anonymous/exports.json
@@ -0,0 +1,85 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 41,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 28
+ }
+ },
+ "range": [
+ 41,
+ 69
+ ],
+ "declaration": {
+ "type": "FunctionDeclaration",
+ "start": 56,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 15
+ },
+ "end": {
+ "line": 4,
+ "column": 28
+ }
+ },
+ "range": [
+ 56,
+ 69
+ ],
+ "id": null,
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 67,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 26
+ },
+ "end": {
+ "line": 4,
+ "column": 28
+ }
+ },
+ "range": [
+ 67,
+ 69
+ ],
+ "body": []
+ }
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-function-named/ast.json b/packages/docgen/src/test/fixtures/default-function-named/ast.json
new file mode 100644
index 0000000000000..a082f805974d2
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-function-named/ast.json
@@ -0,0 +1,148 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 84,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "range": [
+ 41,
+ 83
+ ],
+ "body": [
+ {
+ "type": "ExportDefaultDeclaration",
+ "start": 41,
+ "end": 83,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "range": [
+ 41,
+ 83
+ ],
+ "declaration": {
+ "type": "FunctionDeclaration",
+ "start": 56,
+ "end": 83,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 15
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "range": [
+ 56,
+ 83
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 65,
+ "end": 78,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 24
+ },
+ "end": {
+ "line": 4,
+ "column": 37
+ }
+ },
+ "range": [
+ 65,
+ 78
+ ],
+ "name": "myDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 81,
+ "end": 83,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 40
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "range": [
+ 81,
+ 83
+ ],
+ "body": []
+ }
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "sourceType": "module",
+ "comments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-function-named/code.js b/packages/docgen/src/test/fixtures/default-function-named/code.js
new file mode 100644
index 0000000000000..74318a75d6ab9
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-function-named/code.js
@@ -0,0 +1,4 @@
+/**
+ * Function declaration example.
+ */
+export default function myDeclaration() {}
diff --git a/packages/docgen/src/test/fixtures/default-function-named/exports.json b/packages/docgen/src/test/fixtures/default-function-named/exports.json
new file mode 100644
index 0000000000000..7765cbef0da6d
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-function-named/exports.json
@@ -0,0 +1,104 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 41,
+ "end": 83,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "range": [
+ 41,
+ 83
+ ],
+ "declaration": {
+ "type": "FunctionDeclaration",
+ "start": 56,
+ "end": 83,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 15
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "range": [
+ 56,
+ 83
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 65,
+ "end": 78,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 24
+ },
+ "end": {
+ "line": 4,
+ "column": 37
+ }
+ },
+ "range": [
+ 65,
+ 78
+ ],
+ "name": "myDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 81,
+ "end": 83,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 40
+ },
+ "end": {
+ "line": 4,
+ "column": 42
+ }
+ },
+ "range": [
+ 81,
+ 83
+ ],
+ "body": []
+ }
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-function-named/ir.json b/packages/docgen/src/test/fixtures/default-function-named/ir.json
new file mode 100644
index 0000000000000..526885397806c
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-function-named/ir.json
@@ -0,0 +1,7 @@
+[
+ {
+ "name": "default",
+ "description": "Function declaration example.",
+ "tags": []
+ }
+]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-identifier/ast.json b/packages/docgen/src/test/fixtures/default-identifier/ast.json
new file mode 100644
index 0000000000000..d83f628004f7d
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-identifier/ast.json
@@ -0,0 +1,165 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 98,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 32
+ }
+ },
+ "range": [
+ 38,
+ 97
+ ],
+ "body": [
+ {
+ "type": "ClassDeclaration",
+ "start": 38,
+ "end": 63,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 25
+ }
+ },
+ "range": [
+ 38,
+ 63
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 44,
+ "end": 60,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 6
+ },
+ "end": {
+ "line": 4,
+ "column": 22
+ }
+ },
+ "range": [
+ 44,
+ 60
+ ],
+ "name": "ClassDeclaration"
+ },
+ "superClass": null,
+ "body": {
+ "type": "ClassBody",
+ "start": 61,
+ "end": 63,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 23
+ },
+ "end": {
+ "line": 4,
+ "column": 25
+ }
+ },
+ "range": [
+ 61,
+ 63
+ ],
+ "body": []
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 0,
+ "end": 37,
+ "range": [
+ 0,
+ 37
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportDefaultDeclaration",
+ "start": 65,
+ "end": 97,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 32
+ }
+ },
+ "range": [
+ 65,
+ 97
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 80,
+ "end": 96,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 15
+ },
+ "end": {
+ "line": 6,
+ "column": 31
+ }
+ },
+ "range": [
+ 80,
+ 96
+ ],
+ "name": "ClassDeclaration"
+ }
+ }
+ ],
+ "sourceType": "module",
+ "comments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 0,
+ "end": 37,
+ "range": [
+ 0,
+ 37
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-identifier/code.js b/packages/docgen/src/test/fixtures/default-identifier/code.js
new file mode 100644
index 0000000000000..2a172ecc0578f
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-identifier/code.js
@@ -0,0 +1,6 @@
+/**
+ * Class declaration example.
+ */
+class ClassDeclaration {}
+
+export default ClassDeclaration;
diff --git a/packages/docgen/src/test/fixtures/default-identifier/exports.json b/packages/docgen/src/test/fixtures/default-identifier/exports.json
new file mode 100644
index 0000000000000..c5cb42d5ad4ca
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-identifier/exports.json
@@ -0,0 +1,39 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 65,
+ "end": 97,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 32
+ }
+ },
+ "range": [
+ 65,
+ 97
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 80,
+ "end": 96,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 15
+ },
+ "end": {
+ "line": 6,
+ "column": 31
+ }
+ },
+ "range": [
+ 80,
+ 96
+ ],
+ "name": "ClassDeclaration"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-import-default/ast.json b/packages/docgen/src/test/fixtures/default-import-default/ast.json
new file mode 100644
index 0000000000000..ee77f27a561e7
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-default/ast.json
@@ -0,0 +1,143 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 92,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 29
+ }
+ },
+ "range": [
+ 0,
+ 91
+ ],
+ "body": [
+ {
+ "type": "ImportDeclaration",
+ "start": 0,
+ "end": 60,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 60
+ }
+ },
+ "range": [
+ 0,
+ 60
+ ],
+ "specifiers": [
+ {
+ "type": "ImportDefaultSpecifier",
+ "start": 7,
+ "end": 20,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 7
+ },
+ "end": {
+ "line": 1,
+ "column": 20
+ }
+ },
+ "range": [
+ 7,
+ 20
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 7,
+ "end": 20,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 7
+ },
+ "end": {
+ "line": 1,
+ "column": 20
+ }
+ },
+ "range": [
+ 7,
+ 20
+ ],
+ "name": "fnDeclaration"
+ }
+ }
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 26,
+ "end": 59,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 26
+ },
+ "end": {
+ "line": 1,
+ "column": 59
+ }
+ },
+ "range": [
+ 26,
+ 59
+ ],
+ "value": "./default-import-default-module",
+ "raw": "'./default-import-default-module'"
+ }
+ },
+ {
+ "type": "ExportDefaultDeclaration",
+ "start": 62,
+ "end": 91,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 29
+ }
+ },
+ "range": [
+ 62,
+ 91
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 77,
+ "end": 90,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 15
+ },
+ "end": {
+ "line": 3,
+ "column": 28
+ }
+ },
+ "range": [
+ 77,
+ 90
+ ],
+ "name": "fnDeclaration"
+ }
+ }
+ ],
+ "sourceType": "module",
+ "comments": []
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-import-default/code.js b/packages/docgen/src/test/fixtures/default-import-default/code.js
new file mode 100644
index 0000000000000..70619a579c57b
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-default/code.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import fnDeclaration from './module-code';
+
+export default fnDeclaration;
diff --git a/packages/docgen/src/test/fixtures/default-import-default/exports.json b/packages/docgen/src/test/fixtures/default-import-default/exports.json
new file mode 100644
index 0000000000000..fd453ed301100
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-default/exports.json
@@ -0,0 +1,39 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 62,
+ "end": 91,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 29
+ }
+ },
+ "range": [
+ 62,
+ 91
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 77,
+ "end": 90,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 15
+ },
+ "end": {
+ "line": 3,
+ "column": 28
+ }
+ },
+ "range": [
+ 77,
+ 90
+ ],
+ "name": "fnDeclaration"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-import-default/module-code.js b/packages/docgen/src/test/fixtures/default-import-default/module-code.js
new file mode 100644
index 0000000000000..4da030a5b9d0a
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-default/module-code.js
@@ -0,0 +1,6 @@
+/**
+ * Function declaration.
+ */
+function functionDeclaration() {}
+
+export default functionDeclaration;
diff --git a/packages/docgen/src/test/fixtures/default-import-default/module-ir.json b/packages/docgen/src/test/fixtures/default-import-default/module-ir.json
new file mode 100644
index 0000000000000..9aaa9dc2213f5
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-default/module-ir.json
@@ -0,0 +1,7 @@
+[
+ {
+ "name": "default",
+ "description": "Function declaration.",
+ "tags": []
+ }
+]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-import-named/ast.json b/packages/docgen/src/test/fixtures/default-import-named/ast.json
new file mode 100644
index 0000000000000..73947334d5ac8
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-named/ast.json
@@ -0,0 +1,163 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 117,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 29
+ }
+ },
+ "range": [
+ 0,
+ 116
+ ],
+ "body": [
+ {
+ "type": "ImportDeclaration",
+ "start": 0,
+ "end": 85,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 85
+ }
+ },
+ "range": [
+ 0,
+ 85
+ ],
+ "specifiers": [
+ {
+ "type": "ImportSpecifier",
+ "start": 9,
+ "end": 45,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 45
+ }
+ },
+ "range": [
+ 9,
+ 45
+ ],
+ "imported": {
+ "type": "Identifier",
+ "start": 9,
+ "end": 28,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 28
+ }
+ },
+ "range": [
+ 9,
+ 28
+ ],
+ "name": "functionDeclaration"
+ },
+ "local": {
+ "type": "Identifier",
+ "start": 32,
+ "end": 45,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 32
+ },
+ "end": {
+ "line": 1,
+ "column": 45
+ }
+ },
+ "range": [
+ 32,
+ 45
+ ],
+ "name": "fnDeclaration"
+ }
+ }
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 53,
+ "end": 84,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 53
+ },
+ "end": {
+ "line": 1,
+ "column": 84
+ }
+ },
+ "range": [
+ 53,
+ 84
+ ],
+ "value": "./default-import-named-module",
+ "raw": "'./default-import-named-module'"
+ }
+ },
+ {
+ "type": "ExportDefaultDeclaration",
+ "start": 87,
+ "end": 116,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 29
+ }
+ },
+ "range": [
+ 87,
+ 116
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 102,
+ "end": 115,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 15
+ },
+ "end": {
+ "line": 3,
+ "column": 28
+ }
+ },
+ "range": [
+ 102,
+ 115
+ ],
+ "name": "fnDeclaration"
+ }
+ }
+ ],
+ "sourceType": "module",
+ "comments": []
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-import-named/code.js b/packages/docgen/src/test/fixtures/default-import-named/code.js
new file mode 100644
index 0000000000000..b8e89023444b5
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-named/code.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import { functionDeclaration as fnDeclaration } from './module-code';
+
+export default fnDeclaration;
diff --git a/packages/docgen/src/test/fixtures/default-import-named/exports.json b/packages/docgen/src/test/fixtures/default-import-named/exports.json
new file mode 100644
index 0000000000000..25d0f6024356f
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-named/exports.json
@@ -0,0 +1,39 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 87,
+ "end": 116,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 29
+ }
+ },
+ "range": [
+ 87,
+ 116
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 102,
+ "end": 115,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 15
+ },
+ "end": {
+ "line": 3,
+ "column": 28
+ }
+ },
+ "range": [
+ 102,
+ 115
+ ],
+ "name": "fnDeclaration"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-import-named/module-code.js b/packages/docgen/src/test/fixtures/default-import-named/module-code.js
new file mode 100644
index 0000000000000..7ec5c68c3fbb3
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-named/module-code.js
@@ -0,0 +1,6 @@
+/**
+ * Function declaration.
+ */
+function functionDeclaration() {}
+
+export { functionDeclaration };
diff --git a/packages/docgen/src/test/fixtures/default-import-named/module-ir.json b/packages/docgen/src/test/fixtures/default-import-named/module-ir.json
new file mode 100644
index 0000000000000..3c04255485cb9
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-import-named/module-ir.json
@@ -0,0 +1 @@
+[{"name":"functionDeclaration","description":"Function declaration.","tags":[]}]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-named-export/ast.json b/packages/docgen/src/test/fixtures/default-named-export/ast.json
new file mode 100644
index 0000000000000..31fde5d2e6883
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-named-export/ast.json
@@ -0,0 +1,189 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 119,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 35
+ }
+ },
+ "range": [
+ 41,
+ 118
+ ],
+ "body": [
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 41,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 41,
+ 81
+ ],
+ "declaration": {
+ "type": "FunctionDeclaration",
+ "start": 48,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 7
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 48,
+ 81
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 57,
+ "end": 76,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 16
+ },
+ "end": {
+ "line": 4,
+ "column": 35
+ }
+ },
+ "range": [
+ 57,
+ 76
+ ],
+ "name": "functionDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 79,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 38
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 79,
+ 81
+ ],
+ "body": []
+ }
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportDefaultDeclaration",
+ "start": 83,
+ "end": 118,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 35
+ }
+ },
+ "range": [
+ 83,
+ 118
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 98,
+ "end": 117,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 15
+ },
+ "end": {
+ "line": 6,
+ "column": 34
+ }
+ },
+ "range": [
+ 98,
+ 117
+ ],
+ "name": "functionDeclaration"
+ }
+ }
+ ],
+ "sourceType": "module",
+ "comments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-named-export/code.js b/packages/docgen/src/test/fixtures/default-named-export/code.js
new file mode 100644
index 0000000000000..bd45a875a5eb6
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-named-export/code.js
@@ -0,0 +1,6 @@
+/**
+ * Function declaration example.
+ */
+export function functionDeclaration() {}
+
+export default functionDeclaration;
diff --git a/packages/docgen/src/test/fixtures/default-named-export/exports.json b/packages/docgen/src/test/fixtures/default-named-export/exports.json
new file mode 100644
index 0000000000000..b432555e4d7a4
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-named-export/exports.json
@@ -0,0 +1,147 @@
+[
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 41,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 41,
+ 81
+ ],
+ "declaration": {
+ "type": "FunctionDeclaration",
+ "start": 48,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 7
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 48,
+ 81
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 57,
+ "end": 76,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 16
+ },
+ "end": {
+ "line": 4,
+ "column": 35
+ }
+ },
+ "range": [
+ 57,
+ 76
+ ],
+ "name": "functionDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 79,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 38
+ },
+ "end": {
+ "line": 4,
+ "column": 40
+ }
+ },
+ "range": [
+ 79,
+ 81
+ ],
+ "body": []
+ }
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportDefaultDeclaration",
+ "start": 83,
+ "end": 118,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 35
+ }
+ },
+ "range": [
+ 83,
+ 118
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 98,
+ "end": 117,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 15
+ },
+ "end": {
+ "line": 6,
+ "column": 34
+ }
+ },
+ "range": [
+ 98,
+ 117
+ ],
+ "name": "functionDeclaration"
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-undocumented-nocomments/code.js b/packages/docgen/src/test/fixtures/default-undocumented-nocomments/code.js
new file mode 100644
index 0000000000000..e82c517463fd0
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-undocumented-nocomments/code.js
@@ -0,0 +1,3 @@
+const myDeclaration = function() {};
+
+export default myDeclaration;
diff --git a/packages/docgen/src/test/fixtures/default-undocumented-nocomments/exports.json b/packages/docgen/src/test/fixtures/default-undocumented-nocomments/exports.json
new file mode 100644
index 0000000000000..ac0eef8437933
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-undocumented-nocomments/exports.json
@@ -0,0 +1,39 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 38,
+ "end": 67,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 29
+ }
+ },
+ "range": [
+ 38,
+ 67
+ ],
+ "declaration": {
+ "type": "Identifier",
+ "start": 53,
+ "end": 66,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 15
+ },
+ "end": {
+ "line": 3,
+ "column": 28
+ }
+ },
+ "range": [
+ 53,
+ 66
+ ],
+ "name": "myDeclaration"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-undocumented-oneliner/code.js b/packages/docgen/src/test/fixtures/default-undocumented-oneliner/code.js
new file mode 100644
index 0000000000000..11e8966dd62d1
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-undocumented-oneliner/code.js
@@ -0,0 +1,2 @@
+// This comment should be ignored
+export default function() { }
diff --git a/packages/docgen/src/test/fixtures/default-undocumented-oneliner/exports.json b/packages/docgen/src/test/fixtures/default-undocumented-oneliner/exports.json
new file mode 100644
index 0000000000000..45c6ab79f0913
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-undocumented-oneliner/exports.json
@@ -0,0 +1,85 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 34,
+ "end": 63,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 0
+ },
+ "end": {
+ "line": 2,
+ "column": 29
+ }
+ },
+ "range": [
+ 34,
+ 63
+ ],
+ "declaration": {
+ "type": "FunctionDeclaration",
+ "start": 49,
+ "end": 63,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 15
+ },
+ "end": {
+ "line": 2,
+ "column": 29
+ }
+ },
+ "range": [
+ 49,
+ 63
+ ],
+ "id": null,
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 60,
+ "end": 63,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 26
+ },
+ "end": {
+ "line": 2,
+ "column": 29
+ }
+ },
+ "range": [
+ 60,
+ 63
+ ],
+ "body": []
+ }
+ },
+ "leadingComments": [
+ {
+ "type": "Line",
+ "value": " This comment should be ignored",
+ "start": 0,
+ "end": 33,
+ "range": [
+ 0,
+ 33
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 33
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/default-variable/code.js b/packages/docgen/src/test/fixtures/default-variable/code.js
new file mode 100644
index 0000000000000..2e1a56238583f
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-variable/code.js
@@ -0,0 +1,4 @@
+/**
+ * Variable declaration example.
+ */
+export default true;
diff --git a/packages/docgen/src/test/fixtures/default-variable/exports.json b/packages/docgen/src/test/fixtures/default-variable/exports.json
new file mode 100644
index 0000000000000..e6204ba061364
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/default-variable/exports.json
@@ -0,0 +1,62 @@
+{
+ "type": "ExportDefaultDeclaration",
+ "start": 41,
+ "end": 61,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 20
+ }
+ },
+ "range": [
+ 41,
+ 61
+ ],
+ "declaration": {
+ "type": "Literal",
+ "start": 56,
+ "end": 60,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 15
+ },
+ "end": {
+ "line": 4,
+ "column": 19
+ }
+ },
+ "range": [
+ 56,
+ 60
+ ],
+ "value": true,
+ "raw": "true"
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/markdown/code.js b/packages/docgen/src/test/fixtures/markdown/code.js
new file mode 100644
index 0000000000000..8d4261e0f6328
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/markdown/code.js
@@ -0,0 +1,24 @@
+/**
+ * A function that adds two parameters.
+ *
+ * @deprecated Use native addition instead.
+ * @since v2
+ *
+ * @see addition
+ * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators
+ *
+ * @param {number} firstParam The first param to add.
+ * @param {number} secondParam The second param to add.
+ *
+ * @example
+ *
+ * ```js
+ * const addResult = sum( 1, 3 );
+ * console.log( addResult ); // will yield 4
+ * ```
+ *
+ * @return {number} The result of adding the two params.
+ */
+export const sum = ( firstParam, secondParam ) => {
+ return firstParam + secondParam;
+};
diff --git a/packages/docgen/src/test/fixtures/markdown/docs.md b/packages/docgen/src/test/fixtures/markdown/docs.md
new file mode 100644
index 0000000000000..12baa9046d028
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/markdown/docs.md
@@ -0,0 +1,41 @@
+# Package docs
+
+This is some package docs.
+
+## API Docs
+
+
+
+### sum
+
+[code.js#L22-L24](code.js#L22-L24)
+
+> **Deprecated** Use native addition instead.
+
+A function that adds two parameters.
+
+**Related**
+
+- addition
+-
+
+**Usage**
+
+```js
+const addResult = sum( 1, 3 );
+console.log( addResult ); // will yield 4
+```
+
+**Parameters**
+
+- **firstParam** `number`: The first param to add.
+- **secondParam** `number`: The second param to add.
+
+**Returns**
+
+`number` The result of adding the two params.
+
+
+
+
+After token content.
diff --git a/packages/docgen/src/test/fixtures/named-class/code.js b/packages/docgen/src/test/fixtures/named-class/code.js
new file mode 100644
index 0000000000000..8310ea0c9f7f1
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-class/code.js
@@ -0,0 +1,4 @@
+/**
+ * My declaration example.
+ */
+export class MyDeclaration {}
diff --git a/packages/docgen/src/test/fixtures/named-class/exports.json b/packages/docgen/src/test/fixtures/named-class/exports.json
new file mode 100644
index 0000000000000..e6da37a8974a0
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-class/exports.json
@@ -0,0 +1,103 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 35,
+ "end": 64,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 29
+ }
+ },
+ "range": [
+ 35,
+ 64
+ ],
+ "declaration": {
+ "type": "ClassDeclaration",
+ "start": 42,
+ "end": 64,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 7
+ },
+ "end": {
+ "line": 4,
+ "column": 29
+ }
+ },
+ "range": [
+ 42,
+ 64
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 48,
+ "end": 61,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 13
+ },
+ "end": {
+ "line": 4,
+ "column": 26
+ }
+ },
+ "range": [
+ 48,
+ 61
+ ],
+ "name": "MyDeclaration"
+ },
+ "superClass": null,
+ "body": {
+ "type": "ClassBody",
+ "start": 62,
+ "end": 64,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 27
+ },
+ "end": {
+ "line": 4,
+ "column": 29
+ }
+ },
+ "range": [
+ 62,
+ 64
+ ],
+ "body": []
+ }
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-default-exported/code.js b/packages/docgen/src/test/fixtures/named-default-exported/code.js
new file mode 100644
index 0000000000000..7aebc3a91fa1c
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default-exported/code.js
@@ -0,0 +1 @@
+export { default as moduleName } from './named-default-module';
diff --git a/packages/docgen/src/test/fixtures/named-default-exported/exports.json b/packages/docgen/src/test/fixtures/named-default-exported/exports.json
new file mode 100644
index 0000000000000..5cfbfc1661581
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default-exported/exports.json
@@ -0,0 +1,102 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 0,
+ "end": 63,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 63
+ }
+ },
+ "range": [
+ 0,
+ 63
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 9,
+ "end": 30,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 30
+ }
+ },
+ "range": [
+ 9,
+ 30
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 9,
+ "end": 16,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 16
+ }
+ },
+ "range": [
+ 9,
+ 16
+ ],
+ "name": "default"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 20,
+ "end": 30,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 20
+ },
+ "end": {
+ "line": 1,
+ "column": 30
+ }
+ },
+ "range": [
+ 20,
+ 30
+ ],
+ "name": "moduleName"
+ }
+ }
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 38,
+ "end": 62,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 38
+ },
+ "end": {
+ "line": 1,
+ "column": 62
+ }
+ },
+ "range": [
+ 38,
+ 62
+ ],
+ "value": "./named-default-module",
+ "raw": "'./named-default-module'"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-default-exported/module-code.js b/packages/docgen/src/test/fixtures/named-default-exported/module-code.js
new file mode 100644
index 0000000000000..92127f1f85059
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default-exported/module-code.js
@@ -0,0 +1,4 @@
+/**
+ * Module declaration.
+ */
+export default function( ) {}
diff --git a/packages/docgen/src/test/fixtures/named-default-exported/module-ir.json b/packages/docgen/src/test/fixtures/named-default-exported/module-ir.json
new file mode 100644
index 0000000000000..9e9b2ce30509e
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default-exported/module-ir.json
@@ -0,0 +1,5 @@
+[{
+ "name": "default",
+ "description": "Module declaration.",
+ "tags": []
+}]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-default/code.js b/packages/docgen/src/test/fixtures/named-default/code.js
new file mode 100644
index 0000000000000..441f6e366117f
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default/code.js
@@ -0,0 +1 @@
+export { default } from './module-code';
diff --git a/packages/docgen/src/test/fixtures/named-default/exports.json b/packages/docgen/src/test/fixtures/named-default/exports.json
new file mode 100644
index 0000000000000..fd3a3c79d6a8b
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default/exports.json
@@ -0,0 +1,102 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 0,
+ "end": 49,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 49
+ }
+ },
+ "range": [
+ 0,
+ 49
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 9,
+ "end": 16,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 16
+ }
+ },
+ "range": [
+ 9,
+ 16
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 9,
+ "end": 16,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 16
+ }
+ },
+ "range": [
+ 9,
+ 16
+ ],
+ "name": "default"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 9,
+ "end": 16,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 16
+ }
+ },
+ "range": [
+ 9,
+ 16
+ ],
+ "name": "default"
+ }
+ }
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 24,
+ "end": 48,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 24
+ },
+ "end": {
+ "line": 1,
+ "column": 48
+ }
+ },
+ "range": [
+ 24,
+ 48
+ ],
+ "value": "./named-default-module",
+ "raw": "'./named-default-module'"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-default/module-code.js b/packages/docgen/src/test/fixtures/named-default/module-code.js
new file mode 100644
index 0000000000000..92127f1f85059
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default/module-code.js
@@ -0,0 +1,4 @@
+/**
+ * Module declaration.
+ */
+export default function( ) {}
diff --git a/packages/docgen/src/test/fixtures/named-default/module-ir.json b/packages/docgen/src/test/fixtures/named-default/module-ir.json
new file mode 100644
index 0000000000000..9e9b2ce30509e
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-default/module-ir.json
@@ -0,0 +1,5 @@
+[{
+ "name": "default",
+ "description": "Module declaration.",
+ "tags": []
+}]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-function/code.js b/packages/docgen/src/test/fixtures/named-function/code.js
new file mode 100644
index 0000000000000..eadb279918d04
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-function/code.js
@@ -0,0 +1,4 @@
+/**
+ * My declaration example.
+ */
+export function myDeclaration() {}
diff --git a/packages/docgen/src/test/fixtures/named-function/exports.json b/packages/docgen/src/test/fixtures/named-function/exports.json
new file mode 100644
index 0000000000000..8de0b612c6af6
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-function/exports.json
@@ -0,0 +1,106 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 35,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 34
+ }
+ },
+ "range": [
+ 35,
+ 69
+ ],
+ "declaration": {
+ "type": "FunctionDeclaration",
+ "start": 42,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 7
+ },
+ "end": {
+ "line": 4,
+ "column": 34
+ }
+ },
+ "range": [
+ 42,
+ 69
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 51,
+ "end": 64,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 16
+ },
+ "end": {
+ "line": 4,
+ "column": 29
+ }
+ },
+ "range": [
+ 51,
+ 64
+ ],
+ "name": "myDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 67,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 32
+ },
+ "end": {
+ "line": 4,
+ "column": 34
+ }
+ },
+ "range": [
+ 67,
+ 69
+ ],
+ "body": []
+ }
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-function/ir.json b/packages/docgen/src/test/fixtures/named-function/ir.json
new file mode 100644
index 0000000000000..a9d9ab4175c9c
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-function/ir.json
@@ -0,0 +1,7 @@
+[
+ {
+ "name": "myDeclaration",
+ "description": "My declaration example.",
+ "tags": []
+ }
+]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifier-destructuring/ast.json b/packages/docgen/src/test/fixtures/named-identifier-destructuring/ast.json
new file mode 100644
index 0000000000000..9c17f5ad7401d
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifier-destructuring/ast.json
@@ -0,0 +1,381 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 141,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 44
+ }
+ },
+ "range": [
+ 35,
+ 140
+ ],
+ "body": [
+ {
+ "type": "VariableDeclaration",
+ "start": 35,
+ "end": 94,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 59
+ }
+ },
+ "range": [
+ 35,
+ 94
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 41,
+ "end": 93,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 6
+ },
+ "end": {
+ "line": 4,
+ "column": 58
+ }
+ },
+ "range": [
+ 41,
+ 93
+ ],
+ "id": {
+ "type": "ObjectPattern",
+ "start": 41,
+ "end": 60,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 6
+ },
+ "end": {
+ "line": 4,
+ "column": 25
+ }
+ },
+ "range": [
+ 41,
+ 60
+ ],
+ "properties": [
+ {
+ "type": "Property",
+ "start": 43,
+ "end": 58,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 8
+ },
+ "end": {
+ "line": 4,
+ "column": 23
+ }
+ },
+ "range": [
+ 43,
+ 58
+ ],
+ "method": false,
+ "shorthand": true,
+ "computed": false,
+ "key": {
+ "type": "Identifier",
+ "start": 43,
+ "end": 58,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 8
+ },
+ "end": {
+ "line": 4,
+ "column": 23
+ }
+ },
+ "range": [
+ 43,
+ 58
+ ],
+ "name": "someDeclaration"
+ },
+ "kind": "init",
+ "value": {
+ "type": "Identifier",
+ "start": 43,
+ "end": 58,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 8
+ },
+ "end": {
+ "line": 4,
+ "column": 23
+ }
+ },
+ "range": [
+ 43,
+ 58
+ ],
+ "name": "someDeclaration"
+ }
+ }
+ ]
+ },
+ "init": {
+ "type": "ObjectExpression",
+ "start": 63,
+ "end": 93,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 28
+ },
+ "end": {
+ "line": 4,
+ "column": 58
+ }
+ },
+ "range": [
+ 63,
+ 93
+ ],
+ "properties": [
+ {
+ "type": "Property",
+ "start": 65,
+ "end": 91,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 30
+ },
+ "end": {
+ "line": 4,
+ "column": 56
+ }
+ },
+ "range": [
+ 65,
+ 91
+ ],
+ "method": false,
+ "shorthand": false,
+ "computed": false,
+ "key": {
+ "type": "Identifier",
+ "start": 65,
+ "end": 80,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 30
+ },
+ "end": {
+ "line": 4,
+ "column": 45
+ }
+ },
+ "range": [
+ 65,
+ 80
+ ],
+ "name": "someDeclaration"
+ },
+ "value": {
+ "type": "ArrowFunctionExpression",
+ "start": 82,
+ "end": 91,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 47
+ },
+ "end": {
+ "line": 4,
+ "column": 56
+ }
+ },
+ "range": [
+ 82,
+ 91
+ ],
+ "id": null,
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 88,
+ "end": 91,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 53
+ },
+ "end": {
+ "line": 4,
+ "column": 56
+ }
+ },
+ "range": [
+ 88,
+ 91
+ ],
+ "body": []
+ }
+ },
+ "kind": "init"
+ }
+ ]
+ }
+ }
+ ],
+ "kind": "const",
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 96,
+ "end": 140,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 44
+ }
+ },
+ "range": [
+ 96,
+ 140
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 105,
+ "end": 137,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 41
+ }
+ },
+ "range": [
+ 105,
+ 137
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 105,
+ "end": 120,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 24
+ }
+ },
+ "range": [
+ 105,
+ 120
+ ],
+ "name": "someDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 124,
+ "end": 137,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 28
+ },
+ "end": {
+ "line": 6,
+ "column": 41
+ }
+ },
+ "range": [
+ 124,
+ 137
+ ],
+ "name": "myDeclaration"
+ }
+ }
+ ],
+ "source": null
+ }
+ ],
+ "sourceType": "module",
+ "comments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifier-destructuring/code.js b/packages/docgen/src/test/fixtures/named-identifier-destructuring/code.js
new file mode 100644
index 0000000000000..d5e1b5d46160d
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifier-destructuring/code.js
@@ -0,0 +1,6 @@
+/**
+ * My declaration example.
+ */
+const { someDeclaration } = { someDeclaration: () => { } };
+
+export { someDeclaration as myDeclaration };
diff --git a/packages/docgen/src/test/fixtures/named-identifier-destructuring/exports.json b/packages/docgen/src/test/fixtures/named-identifier-destructuring/exports.json
new file mode 100644
index 0000000000000..ac2695f1c68fa
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifier-destructuring/exports.json
@@ -0,0 +1,82 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 96,
+ "end": 140,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 44
+ }
+ },
+ "range": [
+ 96,
+ 140
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 105,
+ "end": 137,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 41
+ }
+ },
+ "range": [
+ 105,
+ 137
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 105,
+ "end": 120,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 24
+ }
+ },
+ "range": [
+ 105,
+ 120
+ ],
+ "name": "someDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 124,
+ "end": 137,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 28
+ },
+ "end": {
+ "line": 6,
+ "column": 41
+ }
+ },
+ "range": [
+ 124,
+ 137
+ ],
+ "name": "myDeclaration"
+ }
+ }
+ ],
+ "source": null
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifier/ast.json b/packages/docgen/src/test/fixtures/named-identifier/ast.json
new file mode 100644
index 0000000000000..40eb2039185e3
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifier/ast.json
@@ -0,0 +1,211 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 90,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 25
+ }
+ },
+ "range": [
+ 35,
+ 89
+ ],
+ "body": [
+ {
+ "type": "FunctionDeclaration",
+ "start": 35,
+ "end": 62,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 27
+ }
+ },
+ "range": [
+ 35,
+ 62
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 44,
+ "end": 57,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 9
+ },
+ "end": {
+ "line": 4,
+ "column": 22
+ }
+ },
+ "range": [
+ 44,
+ 57
+ ],
+ "name": "myDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 60,
+ "end": 62,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 25
+ },
+ "end": {
+ "line": 4,
+ "column": 27
+ }
+ },
+ "range": [
+ 60,
+ 62
+ ],
+ "body": []
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 64,
+ "end": 89,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 25
+ }
+ },
+ "range": [
+ 64,
+ 89
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 73,
+ "end": 86,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 22
+ }
+ },
+ "range": [
+ 73,
+ 86
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 73,
+ "end": 86,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 22
+ }
+ },
+ "range": [
+ 73,
+ 86
+ ],
+ "name": "myDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 73,
+ "end": 86,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 22
+ }
+ },
+ "range": [
+ 73,
+ 86
+ ],
+ "name": "myDeclaration"
+ }
+ }
+ ],
+ "source": null
+ }
+ ],
+ "sourceType": "module",
+ "comments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifier/code.js b/packages/docgen/src/test/fixtures/named-identifier/code.js
new file mode 100644
index 0000000000000..8fbd063086c19
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifier/code.js
@@ -0,0 +1,6 @@
+/**
+ * My declaration example.
+ */
+function myDeclaration() {}
+
+export { myDeclaration };
diff --git a/packages/docgen/src/test/fixtures/named-identifier/exports.json b/packages/docgen/src/test/fixtures/named-identifier/exports.json
new file mode 100644
index 0000000000000..3c2c7680042cf
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifier/exports.json
@@ -0,0 +1,82 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 64,
+ "end": 89,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 25
+ }
+ },
+ "range": [
+ 64,
+ 89
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 73,
+ "end": 86,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 22
+ }
+ },
+ "range": [
+ 73,
+ 86
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 73,
+ "end": 86,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 22
+ }
+ },
+ "range": [
+ 73,
+ 86
+ ],
+ "name": "myDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 73,
+ "end": 86,
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 9
+ },
+ "end": {
+ "line": 6,
+ "column": 22
+ }
+ },
+ "range": [
+ 73,
+ 86
+ ],
+ "name": "myDeclaration"
+ }
+ }
+ ],
+ "source": null
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifiers-and-inline/ast.json b/packages/docgen/src/test/fixtures/named-identifiers-and-inline/ast.json
new file mode 100644
index 0000000000000..d761a6035d6c2
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifiers-and-inline/ast.json
@@ -0,0 +1,561 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 275,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 16,
+ "column": 40
+ }
+ },
+ "range": [
+ 41,
+ 273
+ ],
+ "body": [
+ {
+ "type": "FunctionDeclaration",
+ "start": 41,
+ "end": 74,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 33
+ }
+ },
+ "range": [
+ 41,
+ 74
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 50,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 9
+ },
+ "end": {
+ "line": 4,
+ "column": 28
+ }
+ },
+ "range": [
+ 50,
+ 69
+ ],
+ "name": "functionDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 72,
+ "end": 74,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 31
+ },
+ "end": {
+ "line": 4,
+ "column": 33
+ }
+ },
+ "range": [
+ 72,
+ 74
+ ],
+ "body": []
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ],
+ "trailingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 76,
+ "end": 113,
+ "range": [
+ 76,
+ 113
+ ],
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 8,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ClassDeclaration",
+ "start": 114,
+ "end": 139,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 0
+ },
+ "end": {
+ "line": 9,
+ "column": 25
+ }
+ },
+ "range": [
+ 114,
+ 139
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 120,
+ "end": 136,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 6
+ },
+ "end": {
+ "line": 9,
+ "column": 22
+ }
+ },
+ "range": [
+ 120,
+ 136
+ ],
+ "name": "ClassDeclaration"
+ },
+ "superClass": null,
+ "body": {
+ "type": "ClassBody",
+ "start": 137,
+ "end": 139,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 23
+ },
+ "end": {
+ "line": 9,
+ "column": 25
+ }
+ },
+ "range": [
+ 137,
+ 139
+ ],
+ "body": []
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 76,
+ "end": 113,
+ "range": [
+ 76,
+ 113
+ ],
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 8,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 141,
+ "end": 190,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 0
+ },
+ "end": {
+ "line": 11,
+ "column": 49
+ }
+ },
+ "range": [
+ 141,
+ 190
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 150,
+ "end": 169,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 9
+ },
+ "end": {
+ "line": 11,
+ "column": 28
+ }
+ },
+ "range": [
+ 150,
+ 169
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 150,
+ "end": 169,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 9
+ },
+ "end": {
+ "line": 11,
+ "column": 28
+ }
+ },
+ "range": [
+ 150,
+ 169
+ ],
+ "name": "functionDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 150,
+ "end": 169,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 9
+ },
+ "end": {
+ "line": 11,
+ "column": 28
+ }
+ },
+ "range": [
+ 150,
+ 169
+ ],
+ "name": "functionDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 171,
+ "end": 187,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 30
+ },
+ "end": {
+ "line": 11,
+ "column": 46
+ }
+ },
+ "range": [
+ 171,
+ 187
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 171,
+ "end": 187,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 30
+ },
+ "end": {
+ "line": 11,
+ "column": 46
+ }
+ },
+ "range": [
+ 171,
+ 187
+ ],
+ "name": "ClassDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 171,
+ "end": 187,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 30
+ },
+ "end": {
+ "line": 11,
+ "column": 46
+ }
+ },
+ "range": [
+ 171,
+ 187
+ ],
+ "name": "ClassDeclaration"
+ }
+ }
+ ],
+ "source": null,
+ "trailingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 192,
+ "end": 232,
+ "range": [
+ 192,
+ 232
+ ],
+ "loc": {
+ "start": {
+ "line": 13,
+ "column": 0
+ },
+ "end": {
+ "line": 15,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 233,
+ "end": 273,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 0
+ },
+ "end": {
+ "line": 16,
+ "column": 40
+ }
+ },
+ "range": [
+ 233,
+ 273
+ ],
+ "declaration": {
+ "type": "VariableDeclaration",
+ "start": 240,
+ "end": 273,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 7
+ },
+ "end": {
+ "line": 16,
+ "column": 40
+ }
+ },
+ "range": [
+ 240,
+ 273
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 246,
+ "end": 272,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 13
+ },
+ "end": {
+ "line": 16,
+ "column": 39
+ }
+ },
+ "range": [
+ 246,
+ 272
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 246,
+ "end": 265,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 13
+ },
+ "end": {
+ "line": 16,
+ "column": 32
+ }
+ },
+ "range": [
+ 246,
+ 265
+ ],
+ "name": "variableDeclaration"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 268,
+ "end": 272,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 35
+ },
+ "end": {
+ "line": 16,
+ "column": 39
+ }
+ },
+ "range": [
+ 268,
+ 272
+ ],
+ "value": true,
+ "raw": "true"
+ }
+ }
+ ],
+ "kind": "const"
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 192,
+ "end": 232,
+ "range": [
+ 192,
+ 232
+ ],
+ "loc": {
+ "start": {
+ "line": 13,
+ "column": 0
+ },
+ "end": {
+ "line": 15,
+ "column": 3
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "sourceType": "module",
+ "comments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ },
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 76,
+ "end": 113,
+ "range": [
+ 76,
+ 113
+ ],
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 8,
+ "column": 3
+ }
+ }
+ },
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 192,
+ "end": 232,
+ "range": [
+ 192,
+ 232
+ ],
+ "loc": {
+ "start": {
+ "line": 13,
+ "column": 0
+ },
+ "end": {
+ "line": 15,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifiers-and-inline/code.js b/packages/docgen/src/test/fixtures/named-identifiers-and-inline/code.js
new file mode 100644
index 0000000000000..10b836d597856
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifiers-and-inline/code.js
@@ -0,0 +1,17 @@
+/**
+ * Function declaration example.
+ */
+function functionDeclaration() {}
+
+/**
+ * Class declaration example.
+ */
+class ClassDeclaration {}
+
+export { functionDeclaration, ClassDeclaration };
+
+/**
+ * Variable declaration example.
+ */
+export const variableDeclaration = true;
+
diff --git a/packages/docgen/src/test/fixtures/named-identifiers-and-inline/exports.json b/packages/docgen/src/test/fixtures/named-identifiers-and-inline/exports.json
new file mode 100644
index 0000000000000..960e07f08fd7c
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifiers-and-inline/exports.json
@@ -0,0 +1,290 @@
+[
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 141,
+ "end": 190,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 0
+ },
+ "end": {
+ "line": 11,
+ "column": 49
+ }
+ },
+ "range": [
+ 141,
+ 190
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 150,
+ "end": 169,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 9
+ },
+ "end": {
+ "line": 11,
+ "column": 28
+ }
+ },
+ "range": [
+ 150,
+ 169
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 150,
+ "end": 169,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 9
+ },
+ "end": {
+ "line": 11,
+ "column": 28
+ }
+ },
+ "range": [
+ 150,
+ 169
+ ],
+ "name": "functionDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 150,
+ "end": 169,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 9
+ },
+ "end": {
+ "line": 11,
+ "column": 28
+ }
+ },
+ "range": [
+ 150,
+ 169
+ ],
+ "name": "functionDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 171,
+ "end": 187,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 30
+ },
+ "end": {
+ "line": 11,
+ "column": 46
+ }
+ },
+ "range": [
+ 171,
+ 187
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 171,
+ "end": 187,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 30
+ },
+ "end": {
+ "line": 11,
+ "column": 46
+ }
+ },
+ "range": [
+ 171,
+ 187
+ ],
+ "name": "ClassDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 171,
+ "end": 187,
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 30
+ },
+ "end": {
+ "line": 11,
+ "column": 46
+ }
+ },
+ "range": [
+ 171,
+ 187
+ ],
+ "name": "ClassDeclaration"
+ }
+ }
+ ],
+ "source": null,
+ "trailingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 192,
+ "end": 232,
+ "range": [
+ 192,
+ 232
+ ],
+ "loc": {
+ "start": {
+ "line": 13,
+ "column": 0
+ },
+ "end": {
+ "line": 15,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 233,
+ "end": 273,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 0
+ },
+ "end": {
+ "line": 16,
+ "column": 40
+ }
+ },
+ "range": [
+ 233,
+ 273
+ ],
+ "declaration": {
+ "type": "VariableDeclaration",
+ "start": 240,
+ "end": 273,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 7
+ },
+ "end": {
+ "line": 16,
+ "column": 40
+ }
+ },
+ "range": [
+ 240,
+ 273
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 246,
+ "end": 272,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 13
+ },
+ "end": {
+ "line": 16,
+ "column": 39
+ }
+ },
+ "range": [
+ 246,
+ 272
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 246,
+ "end": 265,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 13
+ },
+ "end": {
+ "line": 16,
+ "column": 32
+ }
+ },
+ "range": [
+ 246,
+ 265
+ ],
+ "name": "variableDeclaration"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 268,
+ "end": 272,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 35
+ },
+ "end": {
+ "line": 16,
+ "column": 39
+ }
+ },
+ "range": [
+ 268,
+ 272
+ ],
+ "value": true,
+ "raw": "true"
+ }
+ }
+ ],
+ "kind": "const"
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 192,
+ "end": 232,
+ "range": [
+ 192,
+ 232
+ ],
+ "loc": {
+ "start": {
+ "line": 13,
+ "column": 0
+ },
+ "end": {
+ "line": 15,
+ "column": 3
+ }
+ }
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifiers/ast.json b/packages/docgen/src/test/fixtures/named-identifiers/ast.json
new file mode 100644
index 0000000000000..f6aa5b92541ab
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifiers/ast.json
@@ -0,0 +1,599 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 288,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 16,
+ "column": 70
+ }
+ },
+ "range": [
+ 41,
+ 287
+ ],
+ "body": [
+ {
+ "type": "FunctionDeclaration",
+ "start": 41,
+ "end": 74,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 33
+ }
+ },
+ "range": [
+ 41,
+ 74
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 50,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 9
+ },
+ "end": {
+ "line": 4,
+ "column": 28
+ }
+ },
+ "range": [
+ 50,
+ 69
+ ],
+ "name": "functionDeclaration"
+ },
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [],
+ "body": {
+ "type": "BlockStatement",
+ "start": 72,
+ "end": 74,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 31
+ },
+ "end": {
+ "line": 4,
+ "column": 33
+ }
+ },
+ "range": [
+ 72,
+ 74
+ ],
+ "body": []
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ],
+ "trailingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 76,
+ "end": 113,
+ "range": [
+ 76,
+ 113
+ ],
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 8,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ClassDeclaration",
+ "start": 114,
+ "end": 139,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 0
+ },
+ "end": {
+ "line": 9,
+ "column": 25
+ }
+ },
+ "range": [
+ 114,
+ 139
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 120,
+ "end": 136,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 6
+ },
+ "end": {
+ "line": 9,
+ "column": 22
+ }
+ },
+ "range": [
+ 120,
+ 136
+ ],
+ "name": "ClassDeclaration"
+ },
+ "superClass": null,
+ "body": {
+ "type": "ClassBody",
+ "start": 137,
+ "end": 139,
+ "loc": {
+ "start": {
+ "line": 9,
+ "column": 23
+ },
+ "end": {
+ "line": 9,
+ "column": 25
+ }
+ },
+ "range": [
+ 137,
+ 139
+ ],
+ "body": []
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 76,
+ "end": 113,
+ "range": [
+ 76,
+ 113
+ ],
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 8,
+ "column": 3
+ }
+ }
+ }
+ ],
+ "trailingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 141,
+ "end": 181,
+ "range": [
+ 141,
+ 181
+ ],
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 0
+ },
+ "end": {
+ "line": 13,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "VariableDeclaration",
+ "start": 182,
+ "end": 215,
+ "loc": {
+ "start": {
+ "line": 14,
+ "column": 0
+ },
+ "end": {
+ "line": 14,
+ "column": 33
+ }
+ },
+ "range": [
+ 182,
+ 215
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 188,
+ "end": 214,
+ "loc": {
+ "start": {
+ "line": 14,
+ "column": 6
+ },
+ "end": {
+ "line": 14,
+ "column": 32
+ }
+ },
+ "range": [
+ 188,
+ 214
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 188,
+ "end": 207,
+ "loc": {
+ "start": {
+ "line": 14,
+ "column": 6
+ },
+ "end": {
+ "line": 14,
+ "column": 25
+ }
+ },
+ "range": [
+ 188,
+ 207
+ ],
+ "name": "variableDeclaration"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 210,
+ "end": 214,
+ "loc": {
+ "start": {
+ "line": 14,
+ "column": 28
+ },
+ "end": {
+ "line": 14,
+ "column": 32
+ }
+ },
+ "range": [
+ 210,
+ 214
+ ],
+ "value": true,
+ "raw": "true"
+ }
+ }
+ ],
+ "kind": "const",
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 141,
+ "end": 181,
+ "range": [
+ 141,
+ 181
+ ],
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 0
+ },
+ "end": {
+ "line": 13,
+ "column": 3
+ }
+ }
+ }
+ ]
+ },
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 217,
+ "end": 287,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 0
+ },
+ "end": {
+ "line": 16,
+ "column": 70
+ }
+ },
+ "range": [
+ 217,
+ 287
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 226,
+ "end": 245,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 9
+ },
+ "end": {
+ "line": 16,
+ "column": 28
+ }
+ },
+ "range": [
+ 226,
+ 245
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 226,
+ "end": 245,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 9
+ },
+ "end": {
+ "line": 16,
+ "column": 28
+ }
+ },
+ "range": [
+ 226,
+ 245
+ ],
+ "name": "functionDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 226,
+ "end": 245,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 9
+ },
+ "end": {
+ "line": 16,
+ "column": 28
+ }
+ },
+ "range": [
+ 226,
+ 245
+ ],
+ "name": "functionDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 247,
+ "end": 266,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 30
+ },
+ "end": {
+ "line": 16,
+ "column": 49
+ }
+ },
+ "range": [
+ 247,
+ 266
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 247,
+ "end": 266,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 30
+ },
+ "end": {
+ "line": 16,
+ "column": 49
+ }
+ },
+ "range": [
+ 247,
+ 266
+ ],
+ "name": "variableDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 247,
+ "end": 266,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 30
+ },
+ "end": {
+ "line": 16,
+ "column": 49
+ }
+ },
+ "range": [
+ 247,
+ 266
+ ],
+ "name": "variableDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 268,
+ "end": 284,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 51
+ },
+ "end": {
+ "line": 16,
+ "column": 67
+ }
+ },
+ "range": [
+ 268,
+ 284
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 268,
+ "end": 284,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 51
+ },
+ "end": {
+ "line": 16,
+ "column": 67
+ }
+ },
+ "range": [
+ 268,
+ 284
+ ],
+ "name": "ClassDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 268,
+ "end": 284,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 51
+ },
+ "end": {
+ "line": 16,
+ "column": 67
+ }
+ },
+ "range": [
+ 268,
+ 284
+ ],
+ "name": "ClassDeclaration"
+ }
+ }
+ ],
+ "source": null
+ }
+ ],
+ "sourceType": "module",
+ "comments": [
+ {
+ "type": "Block",
+ "value": "*\n * Function declaration example.\n ",
+ "start": 0,
+ "end": 40,
+ "range": [
+ 0,
+ 40
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ },
+ {
+ "type": "Block",
+ "value": "*\n * Class declaration example.\n ",
+ "start": 76,
+ "end": 113,
+ "range": [
+ 76,
+ 113
+ ],
+ "loc": {
+ "start": {
+ "line": 6,
+ "column": 0
+ },
+ "end": {
+ "line": 8,
+ "column": 3
+ }
+ }
+ },
+ {
+ "type": "Block",
+ "value": "*\n * Variable declaration example.\n ",
+ "start": 141,
+ "end": 181,
+ "range": [
+ 141,
+ 181
+ ],
+ "loc": {
+ "start": {
+ "line": 11,
+ "column": 0
+ },
+ "end": {
+ "line": 13,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifiers/code.js b/packages/docgen/src/test/fixtures/named-identifiers/code.js
new file mode 100644
index 0000000000000..6c608a4eea4e7
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifiers/code.js
@@ -0,0 +1,16 @@
+/**
+ * Function declaration example.
+ */
+function functionDeclaration() {}
+
+/**
+ * Class declaration example.
+ */
+class ClassDeclaration {}
+
+/**
+ * Variable declaration example.
+ */
+const variableDeclaration = true;
+
+export { functionDeclaration, variableDeclaration, ClassDeclaration };
diff --git a/packages/docgen/src/test/fixtures/named-identifiers/exports.json b/packages/docgen/src/test/fixtures/named-identifiers/exports.json
new file mode 100644
index 0000000000000..eb1e017e311b4
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifiers/exports.json
@@ -0,0 +1,200 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 217,
+ "end": 287,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 0
+ },
+ "end": {
+ "line": 16,
+ "column": 70
+ }
+ },
+ "range": [
+ 217,
+ 287
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 226,
+ "end": 245,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 9
+ },
+ "end": {
+ "line": 16,
+ "column": 28
+ }
+ },
+ "range": [
+ 226,
+ 245
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 226,
+ "end": 245,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 9
+ },
+ "end": {
+ "line": 16,
+ "column": 28
+ }
+ },
+ "range": [
+ 226,
+ 245
+ ],
+ "name": "functionDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 226,
+ "end": 245,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 9
+ },
+ "end": {
+ "line": 16,
+ "column": 28
+ }
+ },
+ "range": [
+ 226,
+ 245
+ ],
+ "name": "functionDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 247,
+ "end": 266,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 30
+ },
+ "end": {
+ "line": 16,
+ "column": 49
+ }
+ },
+ "range": [
+ 247,
+ 266
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 247,
+ "end": 266,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 30
+ },
+ "end": {
+ "line": 16,
+ "column": 49
+ }
+ },
+ "range": [
+ 247,
+ 266
+ ],
+ "name": "variableDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 247,
+ "end": 266,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 30
+ },
+ "end": {
+ "line": 16,
+ "column": 49
+ }
+ },
+ "range": [
+ 247,
+ 266
+ ],
+ "name": "variableDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 268,
+ "end": 284,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 51
+ },
+ "end": {
+ "line": 16,
+ "column": 67
+ }
+ },
+ "range": [
+ 268,
+ 284
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 268,
+ "end": 284,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 51
+ },
+ "end": {
+ "line": 16,
+ "column": 67
+ }
+ },
+ "range": [
+ 268,
+ 284
+ ],
+ "name": "ClassDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 268,
+ "end": 284,
+ "loc": {
+ "start": {
+ "line": 16,
+ "column": 51
+ },
+ "end": {
+ "line": 16,
+ "column": 67
+ }
+ },
+ "range": [
+ 268,
+ 284
+ ],
+ "name": "ClassDeclaration"
+ }
+ }
+ ],
+ "source": null
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-identifiers/ir.json b/packages/docgen/src/test/fixtures/named-identifiers/ir.json
new file mode 100644
index 0000000000000..ab7e9787fa15f
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-identifiers/ir.json
@@ -0,0 +1 @@
+[{"name":"ClassDeclaration","description":"Class declaration example.","tags":[]},{"name":"functionDeclaration","description":"Function declaration example.","tags":[]},{"name":"variableDeclaration","description":"Variable declaration example.","tags":[]}]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-import-named/code.js b/packages/docgen/src/test/fixtures/named-import-named/code.js
new file mode 100644
index 0000000000000..68e9b4499792c
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-named/code.js
@@ -0,0 +1,5 @@
+export {
+ functionDeclaration,
+ variableDeclaration,
+ ClassDeclaration,
+} from './named-identifiers';
diff --git a/packages/docgen/src/test/fixtures/named-import-named/exports.json b/packages/docgen/src/test/fixtures/named-import-named/exports.json
new file mode 100644
index 0000000000000..8d3340586992f
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-named/exports.json
@@ -0,0 +1,220 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 0,
+ "end": 101,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 5,
+ "column": 29
+ }
+ },
+ "range": [
+ 0,
+ 101
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 10,
+ "end": 29,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 1
+ },
+ "end": {
+ "line": 2,
+ "column": 20
+ }
+ },
+ "range": [
+ 10,
+ 29
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 10,
+ "end": 29,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 1
+ },
+ "end": {
+ "line": 2,
+ "column": 20
+ }
+ },
+ "range": [
+ 10,
+ 29
+ ],
+ "name": "functionDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 10,
+ "end": 29,
+ "loc": {
+ "start": {
+ "line": 2,
+ "column": 1
+ },
+ "end": {
+ "line": 2,
+ "column": 20
+ }
+ },
+ "range": [
+ 10,
+ 29
+ ],
+ "name": "functionDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 32,
+ "end": 51,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 1
+ },
+ "end": {
+ "line": 3,
+ "column": 20
+ }
+ },
+ "range": [
+ 32,
+ 51
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 32,
+ "end": 51,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 1
+ },
+ "end": {
+ "line": 3,
+ "column": 20
+ }
+ },
+ "range": [
+ 32,
+ 51
+ ],
+ "name": "variableDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 32,
+ "end": 51,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 1
+ },
+ "end": {
+ "line": 3,
+ "column": 20
+ }
+ },
+ "range": [
+ 32,
+ 51
+ ],
+ "name": "variableDeclaration"
+ }
+ },
+ {
+ "type": "ExportSpecifier",
+ "start": 54,
+ "end": 70,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 1
+ },
+ "end": {
+ "line": 4,
+ "column": 17
+ }
+ },
+ "range": [
+ 54,
+ 70
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 54,
+ "end": 70,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 1
+ },
+ "end": {
+ "line": 4,
+ "column": 17
+ }
+ },
+ "range": [
+ 54,
+ 70
+ ],
+ "name": "ClassDeclaration"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 54,
+ "end": 70,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 1
+ },
+ "end": {
+ "line": 4,
+ "column": 17
+ }
+ },
+ "range": [
+ 54,
+ 70
+ ],
+ "name": "ClassDeclaration"
+ }
+ }
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 79,
+ "end": 100,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 7
+ },
+ "end": {
+ "line": 5,
+ "column": 28
+ }
+ },
+ "range": [
+ 79,
+ 100
+ ],
+ "value": "./named-identifiers",
+ "raw": "'./named-identifiers'"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-import-namespace/ast.json b/packages/docgen/src/test/fixtures/named-import-namespace/ast.json
new file mode 100644
index 0000000000000..f17b43fcef753
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-namespace/ast.json
@@ -0,0 +1,186 @@
+{
+ "type": "Program",
+ "start": 0,
+ "end": 85,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 21
+ }
+ },
+ "range": [
+ 0,
+ 84
+ ],
+ "body": [
+ {
+ "type": "ImportDeclaration",
+ "start": 0,
+ "end": 61,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 61
+ }
+ },
+ "range": [
+ 0,
+ 61
+ ],
+ "specifiers": [
+ {
+ "type": "ImportNamespaceSpecifier",
+ "start": 7,
+ "end": 21,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 7
+ },
+ "end": {
+ "line": 1,
+ "column": 21
+ }
+ },
+ "range": [
+ 7,
+ 21
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 12,
+ "end": 21,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 12
+ },
+ "end": {
+ "line": 1,
+ "column": 21
+ }
+ },
+ "range": [
+ 12,
+ 21
+ ],
+ "name": "variables"
+ }
+ }
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 27,
+ "end": 60,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 27
+ },
+ "end": {
+ "line": 1,
+ "column": 60
+ }
+ },
+ "range": [
+ 27,
+ 60
+ ],
+ "value": "./named-import-namespace-module",
+ "raw": "'./named-import-namespace-module'"
+ }
+ },
+ {
+ "type": "ExportNamedDeclaration",
+ "start": 63,
+ "end": 84,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 21
+ }
+ },
+ "range": [
+ 63,
+ 84
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 72,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 9
+ },
+ "end": {
+ "line": 3,
+ "column": 18
+ }
+ },
+ "range": [
+ 72,
+ 81
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 72,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 9
+ },
+ "end": {
+ "line": 3,
+ "column": 18
+ }
+ },
+ "range": [
+ 72,
+ 81
+ ],
+ "name": "variables"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 72,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 9
+ },
+ "end": {
+ "line": 3,
+ "column": 18
+ }
+ },
+ "range": [
+ 72,
+ 81
+ ],
+ "name": "variables"
+ }
+ }
+ ],
+ "source": null
+ }
+ ],
+ "sourceType": "module",
+ "comments": []
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-import-namespace/code.js b/packages/docgen/src/test/fixtures/named-import-namespace/code.js
new file mode 100644
index 0000000000000..a6b9234374d42
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-namespace/code.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import * as variables from './named-import-namespace-module';
+
+export { variables };
diff --git a/packages/docgen/src/test/fixtures/named-import-namespace/exports.json b/packages/docgen/src/test/fixtures/named-import-namespace/exports.json
new file mode 100644
index 0000000000000..a2ea24189479d
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-namespace/exports.json
@@ -0,0 +1,82 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 63,
+ "end": 84,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 21
+ }
+ },
+ "range": [
+ 63,
+ 84
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 72,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 9
+ },
+ "end": {
+ "line": 3,
+ "column": 18
+ }
+ },
+ "range": [
+ 72,
+ 81
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 72,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 9
+ },
+ "end": {
+ "line": 3,
+ "column": 18
+ }
+ },
+ "range": [
+ 72,
+ 81
+ ],
+ "name": "variables"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 72,
+ "end": 81,
+ "loc": {
+ "start": {
+ "line": 3,
+ "column": 9
+ },
+ "end": {
+ "line": 3,
+ "column": 18
+ }
+ },
+ "range": [
+ 72,
+ 81
+ ],
+ "name": "variables"
+ }
+ }
+ ],
+ "source": null
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-import-namespace/module-code.js b/packages/docgen/src/test/fixtures/named-import-namespace/module-code.js
new file mode 100644
index 0000000000000..5c29fce852840
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-namespace/module-code.js
@@ -0,0 +1 @@
+export { default as controls } from './default-function-named';
diff --git a/packages/docgen/src/test/fixtures/named-import-namespace/module-exports.json b/packages/docgen/src/test/fixtures/named-import-namespace/module-exports.json
new file mode 100644
index 0000000000000..3e467a9404209
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-namespace/module-exports.json
@@ -0,0 +1,102 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 0,
+ "end": 63,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 63
+ }
+ },
+ "range": [
+ 0,
+ 63
+ ],
+ "declaration": null,
+ "specifiers": [
+ {
+ "type": "ExportSpecifier",
+ "start": 9,
+ "end": 28,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 28
+ }
+ },
+ "range": [
+ 9,
+ 28
+ ],
+ "local": {
+ "type": "Identifier",
+ "start": 9,
+ "end": 16,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 9
+ },
+ "end": {
+ "line": 1,
+ "column": 16
+ }
+ },
+ "range": [
+ 9,
+ 16
+ ],
+ "name": "default"
+ },
+ "exported": {
+ "type": "Identifier",
+ "start": 20,
+ "end": 28,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 20
+ },
+ "end": {
+ "line": 1,
+ "column": 28
+ }
+ },
+ "range": [
+ 20,
+ 28
+ ],
+ "name": "controls"
+ }
+ }
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 36,
+ "end": 62,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 36
+ },
+ "end": {
+ "line": 1,
+ "column": 62
+ }
+ },
+ "range": [
+ 36,
+ 62
+ ],
+ "value": "./default-function-named",
+ "raw": "'./default-function-named'"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-import-namespace/module-ir.json b/packages/docgen/src/test/fixtures/named-import-namespace/module-ir.json
new file mode 100644
index 0000000000000..6488f6035d017
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-import-namespace/module-ir.json
@@ -0,0 +1 @@
+[{"name":"controls","description":"Function declaration example.","tags":[]}]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-variable/code.js b/packages/docgen/src/test/fixtures/named-variable/code.js
new file mode 100644
index 0000000000000..e89c8a9751394
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-variable/code.js
@@ -0,0 +1,5 @@
+/**
+ * My declaration example.
+ */
+export const myDeclaration = true;
+
diff --git a/packages/docgen/src/test/fixtures/named-variable/exports.json b/packages/docgen/src/test/fixtures/named-variable/exports.json
new file mode 100644
index 0000000000000..1c5797990ceab
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-variable/exports.json
@@ -0,0 +1,125 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 35,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 34
+ }
+ },
+ "range": [
+ 35,
+ 69
+ ],
+ "declaration": {
+ "type": "VariableDeclaration",
+ "start": 42,
+ "end": 69,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 7
+ },
+ "end": {
+ "line": 4,
+ "column": 34
+ }
+ },
+ "range": [
+ 42,
+ 69
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 48,
+ "end": 68,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 13
+ },
+ "end": {
+ "line": 4,
+ "column": 33
+ }
+ },
+ "range": [
+ 48,
+ 68
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 48,
+ "end": 61,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 13
+ },
+ "end": {
+ "line": 4,
+ "column": 26
+ }
+ },
+ "range": [
+ 48,
+ 61
+ ],
+ "name": "myDeclaration"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 64,
+ "end": 68,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 29
+ },
+ "end": {
+ "line": 4,
+ "column": 33
+ }
+ },
+ "range": [
+ 64,
+ 68
+ ],
+ "value": true,
+ "raw": "true"
+ }
+ }
+ ],
+ "kind": "const"
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/named-variables/code.js b/packages/docgen/src/test/fixtures/named-variables/code.js
new file mode 100644
index 0000000000000..8f56e0aff46e5
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-variables/code.js
@@ -0,0 +1,6 @@
+/**
+ * My declaration example.
+ */
+export const firstDeclaration = true,
+ secondDeclaration = 42;
+
diff --git a/packages/docgen/src/test/fixtures/named-variables/exports.json b/packages/docgen/src/test/fixtures/named-variables/exports.json
new file mode 100644
index 0000000000000..24d1d1525e950
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/named-variables/exports.json
@@ -0,0 +1,185 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 35,
+ "end": 97,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 5,
+ "column": 24
+ }
+ },
+ "range": [
+ 35,
+ 97
+ ],
+ "declaration": {
+ "type": "VariableDeclaration",
+ "start": 42,
+ "end": 97,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 7
+ },
+ "end": {
+ "line": 5,
+ "column": 24
+ }
+ },
+ "range": [
+ 42,
+ 97
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 48,
+ "end": 71,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 13
+ },
+ "end": {
+ "line": 4,
+ "column": 36
+ }
+ },
+ "range": [
+ 48,
+ 71
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 48,
+ "end": 64,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 13
+ },
+ "end": {
+ "line": 4,
+ "column": 29
+ }
+ },
+ "range": [
+ 48,
+ 64
+ ],
+ "name": "firstDeclaration"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 67,
+ "end": 71,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 32
+ },
+ "end": {
+ "line": 4,
+ "column": 36
+ }
+ },
+ "range": [
+ 67,
+ 71
+ ],
+ "value": true,
+ "raw": "true"
+ }
+ },
+ {
+ "type": "VariableDeclarator",
+ "start": 74,
+ "end": 96,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 1
+ },
+ "end": {
+ "line": 5,
+ "column": 23
+ }
+ },
+ "range": [
+ 74,
+ 96
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 74,
+ "end": 91,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 1
+ },
+ "end": {
+ "line": 5,
+ "column": 18
+ }
+ },
+ "range": [
+ 74,
+ 91
+ ],
+ "name": "secondDeclaration"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 94,
+ "end": 96,
+ "loc": {
+ "start": {
+ "line": 5,
+ "column": 21
+ },
+ "end": {
+ "line": 5,
+ "column": 23
+ }
+ },
+ "range": [
+ 94,
+ 96
+ ],
+ "value": 42,
+ "raw": "42"
+ }
+ }
+ ],
+ "kind": "const"
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * My declaration example.\n ",
+ "start": 0,
+ "end": 34,
+ "range": [
+ 0,
+ 34
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/namespace-commented/code.js b/packages/docgen/src/test/fixtures/namespace-commented/code.js
new file mode 100644
index 0000000000000..5008b26d090aa
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace-commented/code.js
@@ -0,0 +1,4 @@
+/**
+ * This comment should be ignored.
+ */
+export * from './namespace-module';
diff --git a/packages/docgen/src/test/fixtures/namespace-commented/exports.json b/packages/docgen/src/test/fixtures/namespace-commented/exports.json
new file mode 100644
index 0000000000000..37166ddd12a5c
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace-commented/exports.json
@@ -0,0 +1,62 @@
+{
+ "type": "ExportAllDeclaration",
+ "start": 43,
+ "end": 78,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 0
+ },
+ "end": {
+ "line": 4,
+ "column": 35
+ }
+ },
+ "range": [
+ 43,
+ 78
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 57,
+ "end": 77,
+ "loc": {
+ "start": {
+ "line": 4,
+ "column": 14
+ },
+ "end": {
+ "line": 4,
+ "column": 34
+ }
+ },
+ "range": [
+ 57,
+ 77
+ ],
+ "value": "./namespace-module",
+ "raw": "'./namespace-module'"
+ },
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * This comment should be ignored.\n ",
+ "start": 0,
+ "end": 42,
+ "range": [
+ 0,
+ 42
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 3,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/namespace-commented/module-ir.json b/packages/docgen/src/test/fixtures/namespace-commented/module-ir.json
new file mode 100644
index 0000000000000..7798095b482fa
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace-commented/module-ir.json
@@ -0,0 +1,22 @@
+[
+ {
+ "name": "default",
+ "description": "Default variable declaration.",
+ "tags": []
+ },
+ {
+ "name": "MyClass",
+ "description": "Named class.",
+ "tags": []
+ },
+ {
+ "name": "myFunction",
+ "description": "Named function.",
+ "tags": []
+ },
+ {
+ "name": "myVariable",
+ "description": "Named variable.",
+ "tags": []
+ }
+]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/namespace-commented/module.js b/packages/docgen/src/test/fixtures/namespace-commented/module.js
new file mode 100644
index 0000000000000..1ff51c686236a
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace-commented/module.js
@@ -0,0 +1,19 @@
+/**
+ * Named variable.
+ */
+export const myVariable = true;
+
+/**
+ * Named function.
+ */
+export const myFunction = () => {};
+
+/**
+ * Named class.
+ */
+export class MyClass {}
+
+/**
+ * Default variable declaration.
+ */
+export default 42;
diff --git a/packages/docgen/src/test/fixtures/namespace/code.js b/packages/docgen/src/test/fixtures/namespace/code.js
new file mode 100644
index 0000000000000..d2705ea5f8e63
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace/code.js
@@ -0,0 +1 @@
+export * from './namespace-module';
diff --git a/packages/docgen/src/test/fixtures/namespace/exports.json b/packages/docgen/src/test/fixtures/namespace/exports.json
new file mode 100644
index 0000000000000..d6aa32af9ac61
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace/exports.json
@@ -0,0 +1,40 @@
+{
+ "type": "ExportAllDeclaration",
+ "start": 0,
+ "end": 35,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 1,
+ "column": 35
+ }
+ },
+ "range": [
+ 0,
+ 35
+ ],
+ "source": {
+ "type": "Literal",
+ "start": 14,
+ "end": 34,
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 14
+ },
+ "end": {
+ "line": 1,
+ "column": 34
+ }
+ },
+ "range": [
+ 14,
+ 34
+ ],
+ "value": "./namespace-module",
+ "raw": "'./namespace-module'"
+ }
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/namespace/module-ir.json b/packages/docgen/src/test/fixtures/namespace/module-ir.json
new file mode 100644
index 0000000000000..7798095b482fa
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace/module-ir.json
@@ -0,0 +1,22 @@
+[
+ {
+ "name": "default",
+ "description": "Default variable declaration.",
+ "tags": []
+ },
+ {
+ "name": "MyClass",
+ "description": "Named class.",
+ "tags": []
+ },
+ {
+ "name": "myFunction",
+ "description": "Named function.",
+ "tags": []
+ },
+ {
+ "name": "myVariable",
+ "description": "Named variable.",
+ "tags": []
+ }
+]
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/namespace/module.js b/packages/docgen/src/test/fixtures/namespace/module.js
new file mode 100644
index 0000000000000..1ff51c686236a
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/namespace/module.js
@@ -0,0 +1,19 @@
+/**
+ * Named variable.
+ */
+export const myVariable = true;
+
+/**
+ * Named function.
+ */
+export const myFunction = () => {};
+
+/**
+ * Named class.
+ */
+export class MyClass {}
+
+/**
+ * Default variable declaration.
+ */
+export default 42;
diff --git a/packages/docgen/src/test/fixtures/tags-function/code.js b/packages/docgen/src/test/fixtures/tags-function/code.js
new file mode 100644
index 0000000000000..8d4261e0f6328
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/tags-function/code.js
@@ -0,0 +1,24 @@
+/**
+ * A function that adds two parameters.
+ *
+ * @deprecated Use native addition instead.
+ * @since v2
+ *
+ * @see addition
+ * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators
+ *
+ * @param {number} firstParam The first param to add.
+ * @param {number} secondParam The second param to add.
+ *
+ * @example
+ *
+ * ```js
+ * const addResult = sum( 1, 3 );
+ * console.log( addResult ); // will yield 4
+ * ```
+ *
+ * @return {number} The result of adding the two params.
+ */
+export const sum = ( firstParam, secondParam ) => {
+ return firstParam + secondParam;
+};
diff --git a/packages/docgen/src/test/fixtures/tags-function/exports.json b/packages/docgen/src/test/fixtures/tags-function/exports.json
new file mode 100644
index 0000000000000..3d0677a818c4b
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/tags-function/exports.json
@@ -0,0 +1,269 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 521,
+ "end": 609,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 0
+ },
+ "end": {
+ "line": 24,
+ "column": 2
+ }
+ },
+ "range": [
+ 521,
+ 609
+ ],
+ "declaration": {
+ "type": "VariableDeclaration",
+ "start": 528,
+ "end": 609,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 7
+ },
+ "end": {
+ "line": 24,
+ "column": 2
+ }
+ },
+ "range": [
+ 528,
+ 609
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 534,
+ "end": 608,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 13
+ },
+ "end": {
+ "line": 24,
+ "column": 1
+ }
+ },
+ "range": [
+ 534,
+ 608
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 534,
+ "end": 537,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 13
+ },
+ "end": {
+ "line": 22,
+ "column": 16
+ }
+ },
+ "range": [
+ 534,
+ 537
+ ],
+ "name": "sum"
+ },
+ "init": {
+ "type": "ArrowFunctionExpression",
+ "start": 540,
+ "end": 608,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 19
+ },
+ "end": {
+ "line": 24,
+ "column": 1
+ }
+ },
+ "range": [
+ 540,
+ 608
+ ],
+ "id": null,
+ "generator": false,
+ "expression": false,
+ "async": false,
+ "params": [
+ {
+ "type": "Identifier",
+ "start": 542,
+ "end": 552,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 21
+ },
+ "end": {
+ "line": 22,
+ "column": 31
+ }
+ },
+ "range": [
+ 542,
+ 552
+ ],
+ "name": "firstParam"
+ },
+ {
+ "type": "Identifier",
+ "start": 554,
+ "end": 565,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 33
+ },
+ "end": {
+ "line": 22,
+ "column": 44
+ }
+ },
+ "range": [
+ 554,
+ 565
+ ],
+ "name": "secondParam"
+ }
+ ],
+ "body": {
+ "type": "BlockStatement",
+ "start": 571,
+ "end": 608,
+ "loc": {
+ "start": {
+ "line": 22,
+ "column": 50
+ },
+ "end": {
+ "line": 24,
+ "column": 1
+ }
+ },
+ "range": [
+ 571,
+ 608
+ ],
+ "body": [
+ {
+ "type": "ReturnStatement",
+ "start": 574,
+ "end": 606,
+ "loc": {
+ "start": {
+ "line": 23,
+ "column": 1
+ },
+ "end": {
+ "line": 23,
+ "column": 33
+ }
+ },
+ "range": [
+ 574,
+ 606
+ ],
+ "argument": {
+ "type": "BinaryExpression",
+ "start": 581,
+ "end": 605,
+ "loc": {
+ "start": {
+ "line": 23,
+ "column": 8
+ },
+ "end": {
+ "line": 23,
+ "column": 32
+ }
+ },
+ "range": [
+ 581,
+ 605
+ ],
+ "left": {
+ "type": "Identifier",
+ "start": 581,
+ "end": 591,
+ "loc": {
+ "start": {
+ "line": 23,
+ "column": 8
+ },
+ "end": {
+ "line": 23,
+ "column": 18
+ }
+ },
+ "range": [
+ 581,
+ 591
+ ],
+ "name": "firstParam"
+ },
+ "operator": "+",
+ "right": {
+ "type": "Identifier",
+ "start": 594,
+ "end": 605,
+ "loc": {
+ "start": {
+ "line": 23,
+ "column": 21
+ },
+ "end": {
+ "line": 23,
+ "column": 32
+ }
+ },
+ "range": [
+ 594,
+ 605
+ ],
+ "name": "secondParam"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "kind": "const"
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * A function that adds two parameters.\n *\n * @deprecated Use native addition instead.\n * @since v2\n *\n * @see addition\n * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators\n *\n * @param {number} firstParam The first param to add.\n * @param {number} secondParam The second param to add.\n *\n * @example\n *\n * ```js\n * const addResult = sum( 1, 3 );\n * console.log( addResult ); // will yield 4\n * ```\n *\n * @return {number} The result of adding the two params.\n ",
+ "start": 0,
+ "end": 520,
+ "range": [
+ 0,
+ 520
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 21,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/fixtures/tags-variable/code.js b/packages/docgen/src/test/fixtures/tags-variable/code.js
new file mode 100644
index 0000000000000..d2d32717a67bc
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/tags-variable/code.js
@@ -0,0 +1,7 @@
+/**
+ * Constant to document the meaning of life,
+ * the universe and everything else.
+ *
+ * @type {number}
+ */
+export const THE_MEANING = 42;
diff --git a/packages/docgen/src/test/fixtures/tags-variable/exports.json b/packages/docgen/src/test/fixtures/tags-variable/exports.json
new file mode 100644
index 0000000000000..80482610d28e5
--- /dev/null
+++ b/packages/docgen/src/test/fixtures/tags-variable/exports.json
@@ -0,0 +1,125 @@
+{
+ "type": "ExportNamedDeclaration",
+ "start": 111,
+ "end": 141,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 0
+ },
+ "end": {
+ "line": 7,
+ "column": 30
+ }
+ },
+ "range": [
+ 111,
+ 141
+ ],
+ "declaration": {
+ "type": "VariableDeclaration",
+ "start": 118,
+ "end": 141,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 7
+ },
+ "end": {
+ "line": 7,
+ "column": 30
+ }
+ },
+ "range": [
+ 118,
+ 141
+ ],
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 124,
+ "end": 140,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 13
+ },
+ "end": {
+ "line": 7,
+ "column": 29
+ }
+ },
+ "range": [
+ 124,
+ 140
+ ],
+ "id": {
+ "type": "Identifier",
+ "start": 124,
+ "end": 135,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 13
+ },
+ "end": {
+ "line": 7,
+ "column": 24
+ }
+ },
+ "range": [
+ 124,
+ 135
+ ],
+ "name": "THE_MEANING"
+ },
+ "init": {
+ "type": "Literal",
+ "start": 138,
+ "end": 140,
+ "loc": {
+ "start": {
+ "line": 7,
+ "column": 27
+ },
+ "end": {
+ "line": 7,
+ "column": 29
+ }
+ },
+ "range": [
+ 138,
+ 140
+ ],
+ "value": 42,
+ "raw": "42"
+ }
+ }
+ ],
+ "kind": "const"
+ },
+ "specifiers": [],
+ "source": null,
+ "leadingComments": [
+ {
+ "type": "Block",
+ "value": "*\n * Constant to document the meaning of life,\n * the universe and everything else.\n *\n * @type {number}\n ",
+ "start": 0,
+ "end": 110,
+ "range": [
+ 0,
+ 110
+ ],
+ "loc": {
+ "start": {
+ "line": 1,
+ "column": 0
+ },
+ "end": {
+ "line": 6,
+ "column": 3
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/docgen/src/test/formatter-markdown.js b/packages/docgen/src/test/formatter-markdown.js
new file mode 100644
index 0000000000000..cd7e66a12e267
--- /dev/null
+++ b/packages/docgen/src/test/formatter-markdown.js
@@ -0,0 +1,34 @@
+/**
+ * Internal dependencies.
+ */
+const formatter = require( '../markdown/formatter' );
+
+describe( 'Formatter', () => {
+ it( 'returns markdown', () => {
+ const rootDir = '/home/my-path';
+ const docPath = '/home/my-path/docs';
+ const docs = formatter( rootDir, docPath + '-api.md', [ {
+ path: docPath + '-code.js',
+ description: 'My declaration example.',
+ tags: [
+ {
+ title: 'param',
+ description: 'First declaration parameter.',
+ type: 'number',
+ name: 'firstParam',
+ },
+ {
+ title: 'return',
+ description: 'The result of the declaration.',
+ type: 'number',
+ },
+ ],
+ name: 'myDeclaration',
+ lineStart: 1,
+ lineEnd: 2,
+ } ], 'API docs' );
+ expect( docs ).toBe(
+ '# API docs\n\n## myDeclaration\n\n[home/my-path/docs-code.js#L1-L2](home/my-path/docs-code.js#L1-L2)\n\nMy declaration example.\n\n**Parameters**\n\n- **firstParam** `number`: First declaration parameter.\n\n**Returns**\n\n`number` The result of the declaration.\n'
+ );
+ } );
+} );
diff --git a/packages/docgen/src/test/get-export-entries.js b/packages/docgen/src/test/get-export-entries.js
new file mode 100644
index 0000000000000..b1e06f9d145a9
--- /dev/null
+++ b/packages/docgen/src/test/get-export-entries.js
@@ -0,0 +1,352 @@
+/**
+ * Node dependencies.
+ */
+const fs = require( 'fs' );
+const path = require( 'path' );
+
+/**
+ * Internal dependencies.
+ */
+const getExportEntries = require( '../get-export-entries' );
+
+describe( 'Export entries', function() {
+ it( 'default class (anonymous)', () => {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-class-anonymous/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: '*default*',
+ exportName: 'default',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'default class (named)', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-class-named/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'ClassDeclaration',
+ exportName: 'default',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'default function (anonymous)', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-function-anonymous/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: '*default*',
+ exportName: 'default',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'default function (named)', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-function-named/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'myDeclaration',
+ exportName: 'default',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'default identifier', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-identifier/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'ClassDeclaration',
+ exportName: 'default',
+ module: null,
+ lineStart: 6,
+ lineEnd: 6,
+ } );
+ } );
+
+ it( 'default import (named)', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-named/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'fnDeclaration',
+ exportName: 'default',
+ module: null,
+ lineStart: 3,
+ lineEnd: 3,
+ } );
+ } );
+
+ it( 'default import (default)', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-default/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'fnDeclaration',
+ exportName: 'default',
+ module: null,
+ lineStart: 3,
+ lineEnd: 3,
+ } );
+ } );
+
+ it( 'default named export', function() {
+ const tokens = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-named-export/exports.json' ),
+ 'utf-8'
+ );
+ const namedExport = getExportEntries( JSON.parse( tokens )[ 0 ] );
+ expect( namedExport ).toHaveLength( 1 );
+ expect( namedExport[ 0 ] ).toEqual( {
+ localName: 'functionDeclaration',
+ exportName: 'functionDeclaration',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const defaultExport = getExportEntries( JSON.parse( tokens )[ 1 ] );
+ expect( defaultExport ).toHaveLength( 1 );
+ expect( defaultExport[ 0 ] ).toEqual( {
+ localName: 'functionDeclaration',
+ exportName: 'default',
+ module: null,
+ lineStart: 6,
+ lineEnd: 6,
+ } );
+ } );
+
+ it( 'default variable', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-variable/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: '*default*',
+ exportName: 'default',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'named class', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-class/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'MyDeclaration',
+ exportName: 'MyDeclaration',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'named default', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-default/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'default',
+ exportName: 'default',
+ module: './named-default-module',
+ lineStart: 1,
+ lineEnd: 1,
+ } );
+ } );
+
+ it( 'named default (exported)', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-default-exported/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'default',
+ exportName: 'moduleName',
+ module: './named-default-module',
+ lineStart: 1,
+ lineEnd: 1,
+ } );
+ } );
+
+ it( 'named function', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-function/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'myDeclaration',
+ exportName: 'myDeclaration',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'named identifier', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifier/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'myDeclaration',
+ exportName: 'myDeclaration',
+ module: null,
+ lineStart: 6,
+ lineEnd: 6,
+ } );
+ const tokenObject = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifier-destructuring/exports.json' ),
+ 'utf-8'
+ );
+ const nameObject = getExportEntries( JSON.parse( tokenObject ) );
+ expect( nameObject ).toHaveLength( 1 );
+ expect( nameObject[ 0 ] ).toEqual( {
+ localName: 'someDeclaration',
+ exportName: 'myDeclaration',
+ module: null,
+ lineStart: 6,
+ lineEnd: 6,
+ } );
+ } );
+
+ it( 'named identifiers', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifiers/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 3 );
+ expect( name[ 0 ] ).toEqual(
+ { localName: 'functionDeclaration', exportName: 'functionDeclaration', module: null, lineStart: 16, lineEnd: 16 }
+ );
+ expect( name[ 1 ] ).toEqual(
+ { localName: 'variableDeclaration', exportName: 'variableDeclaration', module: null, lineStart: 16, lineEnd: 16 },
+ );
+ expect( name[ 2 ] ).toEqual(
+ { localName: 'ClassDeclaration', exportName: 'ClassDeclaration', module: null, lineStart: 16, lineEnd: 16 },
+ );
+ const tokenIdentifiersAndInline = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifiers-and-inline/exports.json' ),
+ 'utf-8'
+ );
+ const nameInline0 = getExportEntries( JSON.parse( tokenIdentifiersAndInline )[ 0 ] );
+ expect( nameInline0 ).toHaveLength( 2 );
+ expect( nameInline0[ 0 ] ).toEqual(
+ { localName: 'functionDeclaration', exportName: 'functionDeclaration', module: null, lineStart: 11, lineEnd: 11 },
+ );
+ expect( nameInline0[ 1 ] ).toEqual(
+ { localName: 'ClassDeclaration', exportName: 'ClassDeclaration', module: null, lineStart: 11, lineEnd: 11 },
+ );
+ const nameInline1 = getExportEntries( JSON.parse( tokenIdentifiersAndInline )[ 1 ] );
+ expect( nameInline1 ).toHaveLength( 1 );
+ expect( nameInline1[ 0 ] ).toEqual(
+ { localName: 'variableDeclaration', exportName: 'variableDeclaration', module: null, lineStart: 16, lineEnd: 16 },
+ );
+ } );
+
+ it( 'named import namespace', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-import-namespace/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual(
+ { localName: 'variables', exportName: 'variables', module: null, lineStart: 3, lineEnd: 3 },
+ );
+ } );
+
+ it( 'named variable', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-variable/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: 'myDeclaration',
+ exportName: 'myDeclaration',
+ module: null,
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+
+ it( 'named variables', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-variables/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 2 );
+ expect( name[ 0 ] ).toEqual(
+ { localName: 'firstDeclaration', exportName: 'firstDeclaration', module: null, lineStart: 4, lineEnd: 5 },
+ );
+ expect( name[ 1 ] ).toEqual(
+ { localName: 'secondDeclaration', exportName: 'secondDeclaration', module: null, lineStart: 4, lineEnd: 5 },
+ );
+ } );
+
+ it( 'namespace (*)', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/namespace/exports.json' ),
+ 'utf-8'
+ );
+ const name = getExportEntries( JSON.parse( token ) );
+ expect( name ).toHaveLength( 1 );
+ expect( name[ 0 ] ).toEqual( {
+ localName: '*',
+ exportName: null,
+ module: './namespace-module',
+ lineStart: 1,
+ lineEnd: 1,
+ } );
+ } );
+} );
diff --git a/packages/docgen/src/test/get-intermediate-representation.js b/packages/docgen/src/test/get-intermediate-representation.js
new file mode 100644
index 0000000000000..53cb937a2be51
--- /dev/null
+++ b/packages/docgen/src/test/get-intermediate-representation.js
@@ -0,0 +1,454 @@
+/**
+ * Node dependencies.
+ */
+const fs = require( 'fs' );
+const path = require( 'path' );
+
+/**
+ * Internal dependencies.
+ */
+const getIntermediateRepresentation = require( '../get-intermediate-representation' );
+
+describe( 'Intermediate Representation', function() {
+ it( 'undocumented', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-undocumented-nocomments/exports.json' ),
+ 'utf-8'
+ );
+ const ir = getIntermediateRepresentation( null, JSON.parse( token ) );
+ expect( ir ).toHaveLength( 1 );
+ expect( ir[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Undocumented declaration.',
+ tags: [],
+ lineStart: 3,
+ lineEnd: 3,
+ } );
+ const tokenOneliner = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-undocumented-oneliner/exports.json' ),
+ 'utf-8'
+ );
+ const irOneliner = getIntermediateRepresentation( null, JSON.parse( tokenOneliner ) );
+ expect( irOneliner ).toHaveLength( 1 );
+ expect( irOneliner[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Undocumented declaration.',
+ tags: [],
+ lineStart: 2,
+ lineEnd: 2,
+ } );
+ } );
+
+ describe( 'JSDoc in export statement', function() {
+ it( 'default export', function() {
+ const tokenClassAnonymous = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-class-anonymous/exports.json' ),
+ 'utf-8'
+ );
+ const irClassAnonymous = getIntermediateRepresentation( null, JSON.parse( tokenClassAnonymous ) );
+ expect( irClassAnonymous ).toHaveLength( 1 );
+ expect( irClassAnonymous[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Class declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const tokenClassNamed = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-class-named/exports.json' ),
+ 'utf-8'
+ );
+ const irClassNamed = getIntermediateRepresentation( null, JSON.parse( tokenClassNamed ) );
+ expect( irClassNamed ).toHaveLength( 1 );
+ expect( irClassNamed[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Class declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const tokenFnAnonymous = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-function-anonymous/exports.json' ),
+ 'utf-8'
+ );
+ const irFnAnonymous = getIntermediateRepresentation( null, JSON.parse( tokenFnAnonymous ) );
+ expect( irFnAnonymous ).toHaveLength( 1 );
+ expect( irFnAnonymous[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Function declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const tokenFnNamed = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-function-named/exports.json' ),
+ 'utf-8'
+ );
+ const irFnNamed = getIntermediateRepresentation( null, JSON.parse( tokenFnNamed ) );
+ expect( irFnNamed[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Function declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const tokenVariable = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-variable/exports.json' ),
+ 'utf-8'
+ );
+ const irVar = getIntermediateRepresentation( null, JSON.parse( tokenVariable ) );
+ expect( irVar[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Variable declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ } );
+ it( 'named export', function() {
+ const tokenClass = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-class/exports.json' ),
+ 'utf-8'
+ );
+ const irNamedClass = getIntermediateRepresentation( null, JSON.parse( tokenClass ) );
+ expect( irNamedClass ).toHaveLength( 1 );
+ expect( irNamedClass[ 0 ] ).toEqual( {
+ path: null,
+ name: 'MyDeclaration',
+ description: 'My declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const tokenFn = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-function/exports.json' ),
+ 'utf-8'
+ );
+ const irNamedFn = getIntermediateRepresentation( null, JSON.parse( tokenFn ) );
+ expect( irNamedFn ).toHaveLength( 1 );
+ expect( irNamedFn[ 0 ] ).toEqual( {
+ path: null,
+ name: 'myDeclaration',
+ description: 'My declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const tokenVariable = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-variable/exports.json' ),
+ 'utf-8'
+ );
+ const irNamedVar = getIntermediateRepresentation( null, JSON.parse( tokenVariable ) );
+ expect( irNamedVar ).toHaveLength( 1 );
+ expect( irNamedVar[ 0 ] ).toEqual( {
+ path: null,
+ name: 'myDeclaration',
+ description: 'My declaration example.',
+ tags: [],
+ lineStart: 4,
+ lineEnd: 4,
+ } );
+ const tokenVariables = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-variables/exports.json' ),
+ 'utf-8'
+ );
+ const irNamedVars = getIntermediateRepresentation( null, JSON.parse( tokenVariables ) );
+ expect( irNamedVars ).toHaveLength( 2 );
+ expect( irNamedVars[ 0 ] ).toEqual(
+ { path: null, name: 'firstDeclaration', description: 'My declaration example.', tags: [], lineStart: 4, lineEnd: 5 },
+ );
+ expect( irNamedVars[ 1 ] ).toEqual(
+ { path: null, name: 'secondDeclaration', description: 'My declaration example.', tags: [], lineStart: 4, lineEnd: 5 },
+ );
+ } );
+ } );
+
+ describe( 'JSDoc in same file', function() {
+ it( 'default export', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-identifier/exports.json' ),
+ 'utf-8'
+ );
+ const ast = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-identifier/ast.json' ),
+ 'utf-8'
+ );
+ const irDefaultId = getIntermediateRepresentation( null, JSON.parse( token ), JSON.parse( ast ) );
+ expect( irDefaultId ).toHaveLength( 1 );
+ expect( irDefaultId[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Class declaration example.',
+ tags: [],
+ lineStart: 6,
+ lineEnd: 6,
+ } );
+ const namedExport = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-named-export/exports.json' ),
+ 'utf-8'
+ );
+ const namedExportAST = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-named-export/ast.json' ),
+ 'utf-8'
+ );
+ const irDefaultNamed0 = getIntermediateRepresentation( null, JSON.parse( namedExport )[ 0 ], JSON.parse( namedExportAST ) );
+ expect( irDefaultNamed0 ).toHaveLength( 1 );
+ expect( irDefaultNamed0[ 0 ] ).toEqual(
+ { path: null, name: 'functionDeclaration', description: 'Function declaration example.', tags: [], lineStart: 4, lineEnd: 4 }
+ );
+ const irDefaultNamed1 = getIntermediateRepresentation( null, JSON.parse( namedExport )[ 1 ], JSON.parse( namedExportAST ) );
+ expect( irDefaultNamed1[ 0 ] ).toEqual(
+ { path: null, name: 'default', description: 'Function declaration example.', tags: [], lineStart: 6, lineEnd: 6 }
+ );
+ } );
+
+ it( 'named export', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifier/exports.json' ),
+ 'utf-8'
+ );
+ const ast = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifier/ast.json' ),
+ 'utf-8'
+ );
+ const irNamedId = getIntermediateRepresentation( null, JSON.parse( token ), JSON.parse( ast ) );
+ expect( irNamedId ).toHaveLength( 1 );
+ expect( irNamedId[ 0 ] ).toEqual( {
+ path: null,
+ name: 'myDeclaration',
+ description: 'My declaration example.',
+ tags: [],
+ lineStart: 6,
+ lineEnd: 6,
+ } );
+ const tokenObject = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifier-destructuring/exports.json' ),
+ 'utf-8'
+ );
+ const astObject = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifier-destructuring/ast.json' ),
+ 'utf-8'
+ );
+ const irNamedIdDestructuring = getIntermediateRepresentation( null, JSON.parse( tokenObject ), JSON.parse( astObject ) );
+ expect( irNamedIdDestructuring ).toHaveLength( 1 );
+ expect( irNamedIdDestructuring[ 0 ] ).toEqual( {
+ path: null,
+ name: 'myDeclaration',
+ description: 'My declaration example.',
+ tags: [],
+ lineStart: 6,
+ lineEnd: 6,
+ } );
+ const tokens = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifiers/exports.json' ),
+ 'utf-8'
+ );
+ const asts = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifiers/ast.json' ),
+ 'utf-8'
+ );
+ const irIds = getIntermediateRepresentation( null, JSON.parse( tokens ), JSON.parse( asts ) );
+ expect( irIds ).toHaveLength( 3 );
+ expect( irIds[ 0 ] ).toEqual(
+ { path: null, name: 'functionDeclaration', description: 'Function declaration example.', tags: [], lineStart: 16, lineEnd: 16 }
+ );
+ expect( irIds[ 1 ] ).toEqual(
+ { path: null, name: 'variableDeclaration', description: 'Variable declaration example.', tags: [], lineStart: 16, lineEnd: 16 }
+ );
+ expect( irIds[ 2 ] ).toEqual(
+ { path: null, name: 'ClassDeclaration', description: 'Class declaration example.', tags: [], lineStart: 16, lineEnd: 16 }
+ );
+ const foo = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifiers-and-inline/exports.json' ),
+ 'utf-8'
+ );
+ const bar = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifiers-and-inline/ast.json' ),
+ 'utf-8'
+ );
+ const irIdInline0 = getIntermediateRepresentation( null, JSON.parse( foo )[ 0 ], JSON.parse( bar ) );
+ expect( irIdInline0 ).toHaveLength( 2 );
+ expect( irIdInline0[ 0 ] ).toEqual(
+ { path: null, name: 'functionDeclaration', description: 'Function declaration example.', tags: [], lineStart: 11, lineEnd: 11 }
+ );
+ expect( irIdInline0[ 1 ] ).toEqual(
+ { path: null, name: 'ClassDeclaration', description: 'Class declaration example.', tags: [], lineStart: 11, lineEnd: 11 }
+ );
+ const irIdInline1 = getIntermediateRepresentation( null, JSON.parse( foo )[ 1 ], JSON.parse( bar ) );
+ expect( irIdInline1[ 0 ] ).toEqual(
+ { path: null, name: 'variableDeclaration', description: 'Variable declaration example.', tags: [], lineStart: 16, lineEnd: 16 }
+ );
+ } );
+ } );
+
+ describe( 'JSDoc in module dependency', function() {
+ it( 'named export', function() {
+ const tokenImportNamed = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-import-named/exports.json' ),
+ 'utf-8'
+ );
+ const getModuleImportNamed = () => JSON.parse( fs.readFileSync(
+ path.join( __dirname, './fixtures/named-identifiers/ir.json' ),
+ 'utf-8'
+ ) );
+ const ir = getIntermediateRepresentation( null, JSON.parse( tokenImportNamed ), { body: [] }, getModuleImportNamed );
+ expect( ir ).toHaveLength( 3 );
+ expect( ir[ 0 ] ).toEqual(
+ { path: null, name: 'functionDeclaration', description: 'Function declaration example.', tags: [], lineStart: 2, lineEnd: 2 }
+ );
+ expect( ir[ 1 ] ).toEqual(
+ { path: null, name: 'variableDeclaration', description: 'Variable declaration example.', tags: [], lineStart: 3, lineEnd: 3 }
+ );
+ expect( ir[ 2 ] ).toEqual(
+ { path: null, name: 'ClassDeclaration', description: 'Class declaration example.', tags: [], lineStart: 4, lineEnd: 4 }
+
+ );
+ } );
+
+ it( 'named default export', function() {
+ const tokenDefault = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-default/exports.json' ),
+ 'utf-8'
+ );
+ const getModule = () => JSON.parse( fs.readFileSync(
+ path.join( __dirname, './fixtures/named-default/module-ir.json' ),
+ 'utf-8'
+ ) );
+ const irNamedDefault = getIntermediateRepresentation( null, JSON.parse( tokenDefault ), { body: [] }, getModule );
+ expect( irNamedDefault ).toHaveLength( 1 );
+ expect( irNamedDefault[ 0 ] ).toEqual(
+ { path: null, name: 'default', description: 'Module declaration.', tags: [], lineStart: 1, lineEnd: 1 }
+ );
+ const tokenDefaultExported = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-default-exported/exports.json' ),
+ 'utf-8'
+ );
+ const irNamedDefaultExported = getIntermediateRepresentation( null, JSON.parse( tokenDefaultExported ), { body: [] }, getModule );
+ expect( irNamedDefaultExported ).toHaveLength( 1 );
+ expect( irNamedDefaultExported[ 0 ] ).toEqual(
+ { path: null, name: 'moduleName', description: 'Module declaration.', tags: [], lineStart: 1, lineEnd: 1 }
+ );
+ } );
+
+ it( 'namespace export', function() {
+ const token = fs.readFileSync(
+ path.join( __dirname, './fixtures/namespace/exports.json' ),
+ 'utf-8'
+ );
+ const getModule = () => JSON.parse( fs.readFileSync(
+ path.join( __dirname, './fixtures/namespace/module-ir.json' ),
+ 'utf-8'
+ ) );
+ const irNamespace = getIntermediateRepresentation( null, JSON.parse( token ), { body: [] }, getModule );
+ expect( irNamespace ).toHaveLength( 3 );
+ expect( irNamespace[ 0 ] ).toEqual(
+ { path: null, name: 'MyClass', description: 'Named class.', tags: [], lineStart: 1, lineEnd: 1 }
+ );
+ expect( irNamespace[ 1 ] ).toEqual(
+ { path: null, name: 'myFunction', description: 'Named function.', tags: [], lineStart: 1, lineEnd: 1 }
+ );
+ expect( irNamespace[ 2 ] ).toEqual(
+ { path: null, name: 'myVariable', description: 'Named variable.', tags: [], lineStart: 1, lineEnd: 1 }
+ );
+ const tokenCommented = fs.readFileSync(
+ path.join( __dirname, './fixtures/namespace-commented/exports.json' ),
+ 'utf-8'
+ );
+ const irNamespaceCommented = getIntermediateRepresentation( null, JSON.parse( tokenCommented ), { body: [] }, getModule );
+ expect( irNamespaceCommented ).toHaveLength( 3 );
+ expect( irNamespaceCommented[ 0 ] ).toEqual(
+ { path: null, name: 'MyClass', description: 'Named class.', tags: [], lineStart: 4, lineEnd: 4 }
+ );
+ expect( irNamespaceCommented[ 1 ] ).toEqual(
+ { path: null, name: 'myFunction', description: 'Named function.', tags: [], lineStart: 4, lineEnd: 4 }
+ );
+ expect( irNamespaceCommented[ 2 ] ).toEqual(
+ { path: null, name: 'myVariable', description: 'Named variable.', tags: [], lineStart: 4, lineEnd: 4 }
+ );
+ } );
+ } );
+
+ describe( 'JSDoc in module dependency through import', function() {
+ it( 'default export', function() {
+ const tokenDefault = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-default/exports.json' ),
+ 'utf-8'
+ );
+ const astDefault = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-default/ast.json' ),
+ 'utf-8'
+ );
+ const getModuleDefault = () => JSON.parse( fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-default/module-ir.json' ),
+ 'utf-8'
+ ) );
+ const irDefault = getIntermediateRepresentation( null, JSON.parse( tokenDefault ), JSON.parse( astDefault ), getModuleDefault );
+ expect( irDefault ).toHaveLength( 1 );
+ expect( irDefault[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Function declaration.',
+ tags: [],
+ lineStart: 3,
+ lineEnd: 3,
+ } );
+ const tokenNamed = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-named/exports.json' ),
+ 'utf-8'
+ );
+ const astNamed = fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-named/ast.json' ),
+ 'utf-8'
+ );
+ const getModuleNamed = () => JSON.parse( fs.readFileSync(
+ path.join( __dirname, './fixtures/default-import-named/module-ir.json' ),
+ 'utf-8'
+ ) );
+ const irNamed = getIntermediateRepresentation( null, JSON.parse( tokenNamed ), JSON.parse( astNamed ), getModuleNamed );
+ expect( irNamed ).toHaveLength( 1 );
+ expect( irNamed[ 0 ] ).toEqual( {
+ path: null,
+ name: 'default',
+ description: 'Function declaration.',
+ tags: [],
+ lineStart: 3,
+ lineEnd: 3,
+ } );
+ } );
+
+ it( 'named export', function() {
+ const tokenImportNamespace = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-import-namespace/exports.json' ),
+ 'utf-8'
+ );
+ const astImportNamespace = fs.readFileSync(
+ path.join( __dirname, './fixtures/named-import-namespace/ast.json' ),
+ 'utf-8'
+ );
+ const getModuleImportNamespace = ( filePath ) => {
+ if ( filePath === './named-import-namespace-module' ) {
+ return JSON.parse( fs.readFileSync(
+ path.join( __dirname, './fixtures/named-import-namespace/module-ir.json' ),
+ 'utf-8'
+ ) );
+ }
+ return JSON.parse( fs.readFileSync(
+ path.join( __dirname, './fixtures/default-function/ir.json' ),
+ 'utf-8'
+ ) );
+ };
+ const ir = getIntermediateRepresentation( null, JSON.parse( tokenImportNamespace ), JSON.parse( astImportNamespace ), getModuleImportNamespace );
+ expect( ir ).toHaveLength( 1 );
+ expect( ir[ 0 ] ).toEqual(
+ { path: null, name: 'variables', description: 'Undocumented declaration.', tags: [], lineStart: 3, lineEnd: 3 }
+ );
+ } );
+ } );
+} );
diff --git a/packages/docgen/src/test/get-jsdoc-from-token.js b/packages/docgen/src/test/get-jsdoc-from-token.js
new file mode 100644
index 0000000000000..335d63e7935ac
--- /dev/null
+++ b/packages/docgen/src/test/get-jsdoc-from-token.js
@@ -0,0 +1,78 @@
+/**
+ * Internal dependencies.
+ */
+const getJSDocFromToken = require( '../get-jsdoc-from-token' );
+
+describe( 'JSDoc', () => {
+ it( 'extracts description and tags', () => {
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [ {
+ value: '*\n * A function that adds two parameters.\n *\n * @deprecated Use native addition instead.\n * @since v2\n *\n * @see addition\n * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators\n *\n * @param {number} firstParam The first param to add.\n * @param {number} secondParam The second param to add.\n *\n * @example\n *\n * ```js\n * const addResult = sum( 1, 3 );\n * console.log( addResult ); // will yield 4\n * ```\n *\n * @return {number} The result of adding the two params.\n ',
+ } ],
+ } )
+ ).toEqual(
+ {
+ description: 'A function that adds two parameters.',
+ tags: [
+ {
+ title: 'deprecated',
+ description: 'Use native addition instead.',
+ },
+ {
+ title: 'since',
+ description: 'v2',
+ },
+ {
+ title: 'see',
+ description: 'addition',
+ },
+ {
+ title: 'link',
+ description: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators',
+ },
+ {
+ title: 'param',
+ description: 'The first param to add.',
+ type: 'number',
+ name: 'firstParam',
+ },
+ {
+ title: 'param',
+ description: 'The second param to add.',
+ type: 'number',
+ name: 'secondParam',
+ },
+ {
+ title: 'example',
+ description: '```js\nconst addResult = sum( 1, 3 );\nconsole.log( addResult ); // will yield 4\n```',
+ },
+ {
+ title: 'return',
+ description: 'The result of adding the two params.',
+ type: 'number',
+ },
+ ],
+ }
+ );
+
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [ {
+ value: '*\n * Constant to document the meaning of life,\n * the universe and everything else.\n *\n * @type {number}\n ',
+ } ],
+ } )
+ ).toEqual(
+ {
+ description: 'Constant to document the meaning of life,\nthe universe and everything else.',
+ tags: [
+ {
+ title: 'type',
+ description: null,
+ type: 'number',
+ },
+ ],
+ }
+ );
+ } );
+} );
diff --git a/packages/docgen/src/test/get-type-as-string.js b/packages/docgen/src/test/get-type-as-string.js
new file mode 100644
index 0000000000000..6ba09acd33df1
--- /dev/null
+++ b/packages/docgen/src/test/get-type-as-string.js
@@ -0,0 +1,108 @@
+/**
+ * Internal dependencies.
+ */
+const getType = require( '../get-type-as-string' );
+
+describe( 'getType from JSDoc', () => {
+ it( 'NameExpression', () => {
+ const type = getType( {
+ title: 'param',
+ description: 'description',
+ type: {
+ type: 'NameExpression',
+ name: 'Array',
+ },
+ name: 'paramName',
+ } );
+ expect( type ).toBe( 'Array' );
+ } );
+
+ it( 'AllLiteral', () => {
+ const type = getType( {
+ title: 'param',
+ description: 'description',
+ type: {
+ type: 'AllLiteral',
+ },
+ name: 'paramName',
+ } );
+ expect( type ).toBe( '*' );
+ } );
+
+ it( 'Applications', () => {
+ const type = getType( {
+ title: 'param',
+ description: 'description',
+ type: {
+ type: 'TypeApplication',
+ expression: {
+ type: 'NameExpression',
+ name: 'Array',
+ },
+ applications: [
+ {
+ type: 'NameExpression',
+ name: 'Object',
+ },
+ {
+ type: 'NameExpression',
+ name: 'String',
+ },
+ ],
+ },
+ } );
+ expect( type ).toBe( 'Array' );
+ } );
+
+ it( 'NullableType', () => {
+ const type = getType( {
+ title: 'param',
+ description: 'description',
+ type: {
+ type: 'NullableType',
+ expression: {
+ type: 'NameExpression',
+ name: 'string',
+ },
+ prefix: true,
+ },
+ name: 'paramName',
+ } );
+ expect( type ).toBe( '?string' );
+ } );
+
+ it( 'RestType', () => {
+ const type = getType( {
+ title: 'param',
+ description: 'description',
+ type: {
+ type: 'RestType',
+ expression: {
+ type: 'NameExpression',
+ name: 'Function',
+ },
+ },
+ name: 'paramName',
+ } );
+ expect( type ).toBe( '...Function' );
+ } );
+
+ it( 'RestType with UnionType', () => {
+ const type = getType( {
+ title: 'param',
+ description: 'description',
+ type: {
+ type: 'RestType',
+ expression: {
+ type: 'UnionType',
+ elements: [
+ { type: 'NameExpression', name: 'Object' },
+ { type: 'NameExpression', name: 'string' },
+ ],
+ },
+ },
+ name: 'paramName',
+ } );
+ expect( type ).toBe( '...(Object|string)' );
+ } );
+} );
diff --git a/packages/e2e-test-utils/README.md b/packages/e2e-test-utils/README.md
index 6743b099d8abb..4958201be7297 100644
--- a/packages/e2e-test-utils/README.md
+++ b/packages/e2e-test-utils/README.md
@@ -12,6 +12,8 @@ npm install @wordpress/e2e-test-utils --save-dev
## API
+
+
### activatePlugin
[src/index.js#L1-L1](src/index.js#L1-L1)
@@ -76,7 +78,7 @@ Clicks on More Menu item, searches for the button with the text provided and cli
### createEmbeddingMatcher
-[src/index.js#L45-L45](src/index.js#L45-L45)
+[src/index.js#L46-L46](src/index.js#L46-L46)
Creates a function to determine if a request is embedding a certain URL.
@@ -90,7 +92,7 @@ Creates a function to determine if a request is embedding a certain URL.
### createJSONResponse
-[src/index.js#L45-L45](src/index.js#L45-L45)
+[src/index.js#L46-L46](src/index.js#L46-L46)
Respond to a request with a JSON response.
@@ -129,7 +131,7 @@ Creates new URL by parsing base URL, WPPath and query string.
### createURLMatcher
-[src/index.js#L45-L45](src/index.js#L45-L45)
+[src/index.js#L46-L46](src/index.js#L46-L46)
Creates a function to determine if a request is calling a URL with the substring present.
@@ -180,7 +182,7 @@ Verifies that the edit post sidebar is opened, and if it is not, opens it.
`Promise` Promise resolving once the edit post sidebar is opened.
-### findSidebarPanelWithTitle
+### findSidebarPanelToggleButtonWithTitle
[src/index.js#L15-L15](src/index.js#L15-L15)
@@ -194,10 +196,24 @@ Finds a sidebar panel with the provided title.
`?ElementHandle` Object that represents an in-page DOM element.
-### getAllBlocks
+### findSidebarPanelWithTitle
[src/index.js#L16-L16](src/index.js#L16-L16)
+Finds the button responsible for toggling the sidebar panel with the provided title.
+
+**Parameters**
+
+- **panelTitle** `string`: The name of sidebar panel.
+
+**Returns**
+
+`?ElementHandle` Object that represents an in-page DOM element.
+
+### getAllBlocks
+
+[src/index.js#L17-L17](src/index.js#L17-L17)
+
Returns an array with all blocks; Equivalent to calling wp.data.select( 'core/editor' ).getBlocks();
**Returns**
@@ -206,7 +222,7 @@ Returns an array with all blocks; Equivalent to calling wp.data.select( 'core/ed
### getAvailableBlockTransforms
-[src/index.js#L17-L17](src/index.js#L17-L17)
+[src/index.js#L18-L18](src/index.js#L18-L18)
Returns an array of strings with all block titles,
that the current selected block can be transformed into.
@@ -217,7 +233,7 @@ that the current selected block can be transformed into.
### getEditedPostContent
-[src/index.js#L18-L18](src/index.js#L18-L18)
+[src/index.js#L19-L19](src/index.js#L19-L19)
Returns a promise which resolves with the edited post content (HTML string).
@@ -227,7 +243,7 @@ Returns a promise which resolves with the edited post content (HTML string).
### hasBlockSwitcher
-[src/index.js#L19-L19](src/index.js#L19-L19)
+[src/index.js#L20-L20](src/index.js#L20-L20)
Returns a boolean indicating if the current selected block has a block switcher or not.
@@ -237,7 +253,7 @@ Returns a boolean indicating if the current selected block has a block switcher
### insertBlock
-[src/index.js#L20-L20](src/index.js#L20-L20)
+[src/index.js#L21-L21](src/index.js#L21-L21)
Opens the inserter, searches for the given term, then selects the first
result that appears.
@@ -249,7 +265,7 @@ result that appears.
### installPlugin
-[src/index.js#L21-L21](src/index.js#L21-L21)
+[src/index.js#L22-L22](src/index.js#L22-L22)
Installs a plugin from the WP.org repository.
@@ -260,7 +276,7 @@ Installs a plugin from the WP.org repository.
### isCurrentURL
-[src/index.js#L22-L22](src/index.js#L22-L22)
+[src/index.js#L23-L23](src/index.js#L23-L23)
Checks if current URL is a WordPress path.
@@ -275,7 +291,7 @@ Checks if current URL is a WordPress path.
### loginUser
-[src/index.js#L23-L23](src/index.js#L23-L23)
+[src/index.js#L24-L24](src/index.js#L24-L24)
Performs log in with specified username and password.
@@ -286,7 +302,7 @@ Performs log in with specified username and password.
### mockOrTransform
-[src/index.js#L45-L45](src/index.js#L45-L45)
+[src/index.js#L46-L46](src/index.js#L46-L46)
Mocks a request with the supplied mock object, or allows it to run with an optional transform, based on the
deserialised JSON response for the request.
@@ -303,26 +319,26 @@ deserialised JSON response for the request.
### observeFocusLoss
-[src/index.js#L24-L24](src/index.js#L24-L24)
+[src/index.js#L25-L25](src/index.js#L25-L25)
Binds to the document on page load which throws an error if a `focusout`
event occurs without a related target (i.e. focus loss).
### openDocumentSettingsSidebar
-[src/index.js#L25-L25](src/index.js#L25-L25)
+[src/index.js#L26-L26](src/index.js#L26-L26)
Clicks on the button in the header which opens Document Settings sidebar when it is closed.
### openPublishPanel
-[src/index.js#L26-L26](src/index.js#L26-L26)
+[src/index.js#L27-L27](src/index.js#L27-L27)
Opens the publish panel.
### pressKeyTimes
-[src/index.js#L27-L27](src/index.js#L27-L27)
+[src/index.js#L28-L28](src/index.js#L28-L28)
Presses the given keyboard key a number of times in sequence.
@@ -337,7 +353,7 @@ Presses the given keyboard key a number of times in sequence.
### pressKeyWithModifier
-[src/index.js#L28-L28](src/index.js#L28-L28)
+[src/index.js#L29-L29](src/index.js#L29-L29)
Performs a key press with modifier (Shift, Control, Meta, Alt), where each modifier
is normalized to platform-specific modifier.
@@ -349,7 +365,7 @@ is normalized to platform-specific modifier.
### publishPost
-[src/index.js#L29-L29](src/index.js#L29-L29)
+[src/index.js#L30-L30](src/index.js#L30-L30)
Publishes the post, resolving once the request is complete (once a notice
is displayed).
@@ -360,7 +376,7 @@ is displayed).
### publishPostWithPrePublishChecksDisabled
-[src/index.js#L30-L30](src/index.js#L30-L30)
+[src/index.js#L31-L31](src/index.js#L31-L31)
Publishes the post without the pre-publish checks,
resolving once the request is complete (once a notice is displayed).
@@ -371,7 +387,7 @@ resolving once the request is complete (once a notice is displayed).
### saveDraft
-[src/index.js#L31-L31](src/index.js#L31-L31)
+[src/index.js#L32-L32](src/index.js#L32-L32)
Saves the post as a draft, resolving once the request is complete (once the
"Saved" indicator is displayed).
@@ -382,7 +398,7 @@ Saves the post as a draft, resolving once the request is complete (once the
### searchForBlock
-[src/index.js#L32-L32](src/index.js#L32-L32)
+[src/index.js#L33-L33](src/index.js#L33-L33)
Search for block in the global inserter
@@ -392,7 +408,7 @@ Search for block in the global inserter
### selectBlockByClientId
-[src/index.js#L33-L33](src/index.js#L33-L33)
+[src/index.js#L34-L34](src/index.js#L34-L34)
Given the clientId of a block, selects the block on the editor.
@@ -402,7 +418,7 @@ Given the clientId of a block, selects the block on the editor.
### setBrowserViewport
-[src/index.js#L34-L34](src/index.js#L34-L34)
+[src/index.js#L35-L35](src/index.js#L35-L35)
Sets browser viewport to specified type.
@@ -412,7 +428,7 @@ Sets browser viewport to specified type.
### setPostContent
-[src/index.js#L35-L35](src/index.js#L35-L35)
+[src/index.js#L36-L36](src/index.js#L36-L36)
Sets code editor content
@@ -426,7 +442,7 @@ Sets code editor content
### setUpResponseMocking
-[src/index.js#L45-L45](src/index.js#L45-L45)
+[src/index.js#L46-L46](src/index.js#L46-L46)
Sets up mock checks and responses. Accepts a list of mock settings with the following properties:
@@ -457,7 +473,7 @@ If none of the mock settings match the request, the request is allowed to contin
### switchEditorModeTo
-[src/index.js#L36-L36](src/index.js#L36-L36)
+[src/index.js#L37-L37](src/index.js#L37-L37)
Switches editor mode.
@@ -467,21 +483,21 @@ Switches editor mode.
### switchUserToAdmin
-[src/index.js#L37-L37](src/index.js#L37-L37)
+[src/index.js#L38-L38](src/index.js#L38-L38)
Switches the current user to the admin user (if the user
running the test is not already the admin user).
### switchUserToTest
-[src/index.js#L38-L38](src/index.js#L38-L38)
+[src/index.js#L39-L39](src/index.js#L39-L39)
Switches the current user to whichever user we should be
running the tests as (if we're not already that user).
### toggleScreenOption
-[src/index.js#L39-L39](src/index.js#L39-L39)
+[src/index.js#L40-L40](src/index.js#L40-L40)
Toggles the screen option with the given label.
@@ -492,7 +508,7 @@ Toggles the screen option with the given label.
### transformBlockTo
-[src/index.js#L40-L40](src/index.js#L40-L40)
+[src/index.js#L41-L41](src/index.js#L41-L41)
Converts editor's block type.
@@ -502,7 +518,7 @@ Converts editor's block type.
### uninstallPlugin
-[src/index.js#L41-L41](src/index.js#L41-L41)
+[src/index.js#L42-L42](src/index.js#L42-L42)
Uninstalls a plugin.
@@ -512,7 +528,7 @@ Uninstalls a plugin.
### visitAdminPage
-[src/index.js#L42-L42](src/index.js#L42-L42)
+[src/index.js#L43-L43](src/index.js#L43-L43)
Visits admin page; if user is not logged in then it logging in it first, then visits admin page.
@@ -523,7 +539,7 @@ Visits admin page; if user is not logged in then it logging in it first, then vi
### waitForWindowDimensions
-[src/index.js#L43-L43](src/index.js#L43-L43)
+[src/index.js#L44-L44](src/index.js#L44-L44)
Function that waits until the page viewport has the required dimensions.
It is being used to address a problem where after using setViewport the execution may continue,
@@ -535,4 +551,7 @@ without the new dimensions being applied.
- **width** `number`: Width of the window.
- **height** `height`: Height of the window.
+
+
+
diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json
index f31d67677475e..0a6871bff1d76 100644
--- a/packages/e2e-test-utils/package.json
+++ b/packages/e2e-test-utils/package.json
@@ -33,11 +33,17 @@
"lodash": "^4.17.11",
"node-fetch": "^1.7.3"
},
+ "devDependencies": {
+ "@wordpress/docgen": "file:../docgen"
+ },
"peerDependencies": {
"jest": ">=24",
"puppeteer": ">=1.6"
},
"publishConfig": {
"access": "public"
+ },
+ "scripts": {
+ "docs:generate": "docgen ./src/index.js --output ./README.md --to-token"
}
}
From 35815e05a17f36f26d99257dbf6bcc8421921b6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9s?=
Date: Fri, 1 Mar 2019 18:51:34 +0100
Subject: [PATCH 052/169] Update nosolosw notifs (#14196)
---
.github/CODEOWNERS | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 945b03b7b08d8..ce69bf49c1b59 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -23,11 +23,12 @@
# Tooling
/bin @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
-/packages/babel-plugin-import-jsx-pragma @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
+/packages/babel-plugin-import-jsx-pragma @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @nosolosw
/packages/babel-plugin-makepot @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
-/packages/babel-preset-default @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
+/packages/babel-preset-default @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @nosolosw
/packages/browserslist-config @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
/packages/custom-templated-path-webpack-plugin @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
+/packages/docgen @nosolosw
/packages/e2e-test-utils @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
/packages/e2e-tests @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @talldan
/packages/eslint-plugin @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @nosolosw
From aff8782e277350ccc8d2d2f0f67c13c3c670de55 Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Fri, 1 Mar 2019 19:34:49 +0100
Subject: [PATCH 053/169] Make IconButton able to be referenced. (#14163)
* Make IconButton able to get referenced.
* Components: Forward IconButton ref as stateless function
* Components: Restore IconButton aria-pressed prop pass-through
---
.../src/form-file-upload/test/index.js | 2 +-
packages/components/src/icon-button/index.js | 94 ++++++++++---------
.../components/src/icon-button/test/index.js | 21 ++++-
.../test/__snapshots__/index.js.snap | 8 +-
.../notice/test/__snapshots__/index.js.snap | 2 +-
.../test/__snapshots__/index.js.snap | 4 +-
.../src/components/block-mover/test/index.js | 4 +-
.../test/__snapshots__/index.js.snap | 4 +-
.../components/block-switcher/test/index.js | 4 +-
.../test/__snapshots__/index.js.snap | 10 +-
.../src/components/url-input/test/button.js | 6 +-
.../test/__snapshots__/index.js.snap | 4 +-
.../dot-tip/test/__snapshots__/index.js.snap | 2 +-
.../nux/src/components/dot-tip/test/index.js | 2 +-
14 files changed, 96 insertions(+), 71 deletions(-)
diff --git a/packages/components/src/form-file-upload/test/index.js b/packages/components/src/form-file-upload/test/index.js
index bbd843a640827..5a81c898d81bb 100644
--- a/packages/components/src/form-file-upload/test/index.js
+++ b/packages/components/src/form-file-upload/test/index.js
@@ -22,7 +22,7 @@ describe( 'InserterMenu', () => {
);
- const iconButton = wrapper.find( 'IconButton' );
+ const iconButton = wrapper.find( 'ForwardRef(IconButton)' );
const input = wrapper.find( 'input' );
expect( iconButton.prop( 'children' ) ).toBe( 'My Upload Button' );
expect( input.prop( 'style' ).display ).toBe( 'none' );
diff --git a/packages/components/src/icon-button/index.js b/packages/components/src/icon-button/index.js
index 7b0a4ae3541a9..f64d7f6777e93 100644
--- a/packages/components/src/icon-button/index.js
+++ b/packages/components/src/icon-button/index.js
@@ -7,7 +7,7 @@ import { isArray, isString } from 'lodash';
/**
* WordPress dependencies
*/
-import { Component } from '@wordpress/element';
+import { forwardRef } from '@wordpress/element';
/**
* Internal dependencies
@@ -16,50 +16,60 @@ import Tooltip from '../tooltip';
import Button from '../button';
import Dashicon from '../dashicon';
-// This is intentionally a Component class, not a function component because it
-// is common to apply a ref to the button element (only supported in class)
-class IconButton extends Component {
- render() {
- const { icon, children, label, className, tooltip, shortcut, labelPosition, ...additionalProps } = this.props;
- const { 'aria-pressed': ariaPressed } = this.props;
- const classes = classnames( 'components-icon-button', className, {
- 'has-text': children,
- } );
- const tooltipText = tooltip || label;
+function IconButton( props, ref ) {
+ const {
+ icon,
+ children,
+ label,
+ className,
+ tooltip,
+ shortcut,
+ labelPosition,
+ ...additionalProps
+ } = props;
+ const { 'aria-pressed': ariaPressed } = additionalProps;
+ const classes = classnames( 'components-icon-button', className, {
+ 'has-text': children,
+ } );
+ const tooltipText = tooltip || label;
- // Should show the tooltip if...
- const showTooltip = ! additionalProps.disabled && (
- // an explicit tooltip is passed or...
- tooltip ||
- // there's a shortcut or...
- shortcut ||
- (
- // there's a label and...
- !! label &&
- // the children are empty and...
- ( ! children || ( isArray( children ) && ! children.length ) ) &&
- // the tooltip is not explicitly disabled.
- false !== tooltip
- )
- );
-
- let element = (
-
- { isString( icon ) ? : icon }
- { children }
-
- );
+ // Should show the tooltip if...
+ const showTooltip = ! additionalProps.disabled && (
+ // an explicit tooltip is passed or...
+ tooltip ||
+ // there's a shortcut or...
+ shortcut ||
+ (
+ // there's a label and...
+ !! label &&
+ // the children are empty and...
+ ( ! children || ( isArray( children ) && ! children.length ) ) &&
+ // the tooltip is not explicitly disabled.
+ false !== tooltip
+ )
+ );
- if ( showTooltip ) {
- element = (
-
- { element }
-
- );
- }
+ let element = (
+
+ { isString( icon ) ? : icon }
+ { children }
+
+ );
- return element;
+ if ( showTooltip ) {
+ element = (
+
+ { element }
+
+ );
}
+
+ return element;
}
-export default IconButton;
+export default forwardRef( IconButton );
diff --git a/packages/components/src/icon-button/test/index.js b/packages/components/src/icon-button/test/index.js
index 133224941c1b5..e824ed1d556ec 100644
--- a/packages/components/src/icon-button/test/index.js
+++ b/packages/components/src/icon-button/test/index.js
@@ -2,6 +2,12 @@
* External dependencies
*/
import { shallow } from 'enzyme';
+import TestUtils from 'react-dom/test-utils';
+
+/**
+ * WordPress dependencies
+ */
+import { createRef } from '@wordpress/element';
/**
* Internal dependencies
@@ -49,9 +55,11 @@ describe( 'IconButton', () => {
expect( iconButton.hasClass( 'test' ) ).toBe( true );
} );
- it( 'should add an additonal prop to the IconButton element', () => {
- const iconButton = shallow( );
- expect( iconButton.props().test ).toBe( 'test' );
+ it( 'should pass additional props to the underlying button', () => {
+ const iconButton = shallow( );
+
+ expect( iconButton.find( 'ForwardRef(Button)' ).prop( 'aria-pressed' ) ).toBe( 'true' );
+ expect( iconButton.find( 'ForwardRef(Button)' ).prop( 'disabled' ) ).toBe( true );
} );
it( 'should allow custom tooltip text', () => {
@@ -72,5 +80,12 @@ describe( 'IconButton', () => {
expect( iconButton.name() ).toBe( 'Tooltip' );
expect( iconButton.prop( 'text' ) ).toBe( 'WordPress' );
} );
+
+ it( 'forwards ref', () => {
+ const ref = createRef();
+
+ TestUtils.renderIntoDocument( );
+ expect( ref.current.type ).toBe( 'button' );
+ } );
} );
} );
diff --git a/packages/components/src/menu-item/test/__snapshots__/index.js.snap b/packages/components/src/menu-item/test/__snapshots__/index.js.snap
index caa467a67a66b..a0f00b2889f00 100644
--- a/packages/components/src/menu-item/test/__snapshots__/index.js.snap
+++ b/packages/components/src/menu-item/test/__snapshots__/index.js.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MenuItem should match snapshot when all props provided 1`] = `
-
-
+
`;
exports[`MenuItem should match snapshot when info is provided 1`] = `
@@ -40,7 +40,7 @@ exports[`MenuItem should match snapshot when info is provided 1`] = `
`;
exports[`MenuItem should match snapshot when isSelected and role are optionally provided 1`] = `
-
-
+
`;
exports[`MenuItem should match snapshot when only label provided 1`] = `
diff --git a/packages/components/src/notice/test/__snapshots__/index.js.snap b/packages/components/src/notice/test/__snapshots__/index.js.snap
index 28b35679ceec6..fe2329681dd54 100644
--- a/packages/components/src/notice/test/__snapshots__/index.js.snap
+++ b/packages/components/src/notice/test/__snapshots__/index.js.snap
@@ -17,7 +17,7 @@ exports[`Notice should match snapshot 1`] = `
View
-
-
-
+
diff --git a/packages/editor/src/components/block-mover/test/index.js b/packages/editor/src/components/block-mover/test/index.js
index ea13280f4d311..ffb4ef6ab8198 100644
--- a/packages/editor/src/components/block-mover/test/index.js
+++ b/packages/editor/src/components/block-mover/test/index.js
@@ -37,9 +37,9 @@ describe( 'BlockMover', () => {
const moveDown = blockMover.childAt( 2 );
const moveUpDesc = blockMover.childAt( 3 );
const moveDownDesc = blockMover.childAt( 4 );
- expect( moveUp.type().name ).toBe( 'IconButton' );
+ expect( moveUp.name() ).toBe( 'ForwardRef(IconButton)' );
expect( drag.type().name ).toBe( 'IconDragHandle' );
- expect( moveDown.type().name ).toBe( 'IconButton' );
+ expect( moveDown.name() ).toBe( 'ForwardRef(IconButton)' );
expect( moveUp.props() ).toMatchObject( {
className: 'editor-block-mover__control',
onClick: undefined,
diff --git a/packages/editor/src/components/block-switcher/test/__snapshots__/index.js.snap b/packages/editor/src/components/block-switcher/test/__snapshots__/index.js.snap
index 9e03c4e31fb0d..df5a8d415abad 100644
--- a/packages/editor/src/components/block-switcher/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/block-switcher/test/__snapshots__/index.js.snap
@@ -2,7 +2,7 @@
exports[`BlockSwitcher should render disabled block switcher with multi block of different types when no transforms 1`] = `
-
-
+
`;
diff --git a/packages/editor/src/components/block-switcher/test/index.js b/packages/editor/src/components/block-switcher/test/index.js
index 47f784240db2b..544d403872206 100644
--- a/packages/editor/src/components/block-switcher/test/index.js
+++ b/packages/editor/src/components/block-switcher/test/index.js
@@ -171,7 +171,7 @@ describe( 'BlockSwitcher', () => {
test( 'should simulate a keydown event, which should call onToggle and open transform toggle.', () => {
const toggleClosed = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: false } ) );
- const iconButtonClosed = toggleClosed.find( 'IconButton' );
+ const iconButtonClosed = toggleClosed.find( 'ForwardRef(IconButton)' );
iconButtonClosed.simulate( 'keydown', mockKeyDown );
@@ -180,7 +180,7 @@ describe( 'BlockSwitcher', () => {
test( 'should simulate a click event, which should call onToggle.', () => {
const toggleOpen = shallow( getDropdown().props().renderToggle( { onToggle: onToggleStub, isOpen: true } ) );
- const iconButtonOpen = toggleOpen.find( 'IconButton' );
+ const iconButtonOpen = toggleOpen.find( 'ForwardRef(IconButton)' );
iconButtonOpen.simulate( 'keydown', mockKeyDown );
diff --git a/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap b/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap
index 574787b931a8d..752b03174a55f 100644
--- a/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/post-publish-panel/test/__snapshots__/index.js.snap
@@ -12,7 +12,7 @@ exports[`PostPublishPanel should render the post-publish panel if the post is pu
>
Published
-
Scheduled
-
-
-
- {
- const clickEditLink = ( wrapper ) => wrapper.find( 'IconButton.components-toolbar__control' ).simulate( 'click' );
+ const clickEditLink = ( wrapper ) => wrapper.find( 'ForwardRef(IconButton).components-toolbar__control' ).simulate( 'click' );
it( 'should have a valid class name in the wrapper tag', () => {
const wrapper = shallow( );
@@ -20,11 +20,11 @@ describe( 'URLInputButton', () => {
} );
it( 'should not have is-active class when url prop not defined', () => {
const wrapper = shallow( );
- expect( wrapper.find( 'IconButton' ).hasClass( 'is-active' ) ).toBe( false );
+ expect( wrapper.find( 'ForwardRef(IconButton)' ).hasClass( 'is-active' ) ).toBe( false );
} );
it( 'should have is-active class name if url prop defined', () => {
const wrapper = shallow( );
- expect( wrapper.find( 'IconButton' ).hasClass( 'is-active' ) ).toBe( true );
+ expect( wrapper.find( 'ForwardRef(IconButton)' ).hasClass( 'is-active' ) ).toBe( true );
} );
it( 'should have hidden form by default', () => {
const wrapper = shallow( );
diff --git a/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap b/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap
index f8f0aa9027bdc..ccb4526f87457 100644
--- a/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/url-popover/test/__snapshots__/index.js.snap
@@ -13,7 +13,7 @@ exports[`URLPopover matches the snapshot in its default state 1`] = `
Editor
-
Editor
-
- {
It looks like you’re writing a letter. Would you like help?
);
- wrapper.find( 'IconButton[label="Disable tips"]' ).first().simulate( 'click' );
+ wrapper.find( 'ForwardRef(IconButton)[label="Disable tips"]' ).first().simulate( 'click' );
expect( onDisable ).toHaveBeenCalled();
} );
} );
From 54e34bf5fc566339cfcce3910b52a19e7a77d27f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ella=20Van=C2=A0Durpe?=
Date: Fri, 1 Mar 2019 19:59:13 +0100
Subject: [PATCH 054/169] Paste: preserve empty table cells (#14137)
---
packages/block-library/src/table/index.js | 6 ++++++
packages/blocks/src/api/raw-handling/utils.js | 10 ++++++++--
test/integration/fixtures/markdown-in.txt | 4 ++++
test/integration/fixtures/markdown-out.html | 4 ++++
4 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/packages/block-library/src/table/index.js b/packages/block-library/src/table/index.js
index 6cf466f50dc36..78bc014bd80b0 100644
--- a/packages/block-library/src/table/index.js
+++ b/packages/block-library/src/table/index.js
@@ -18,11 +18,14 @@ import edit from './edit';
const tableContentPasteSchema = {
tr: {
+ allowEmpty: true,
children: {
th: {
+ allowEmpty: true,
children: getPhrasingContentSchema(),
},
td: {
+ allowEmpty: true,
children: getPhrasingContentSchema(),
},
},
@@ -33,12 +36,15 @@ const tablePasteSchema = {
table: {
children: {
thead: {
+ allowEmpty: true,
children: tableContentPasteSchema,
},
tfoot: {
+ allowEmpty: true,
children: tableContentPasteSchema,
},
tbody: {
+ allowEmpty: true,
children: tableContentPasteSchema,
},
},
diff --git a/packages/blocks/src/api/raw-handling/utils.js b/packages/blocks/src/api/raw-handling/utils.js
index 4395c82c429dc..896bb6b8d4a5e 100644
--- a/packages/blocks/src/api/raw-handling/utils.js
+++ b/packages/blocks/src/api/raw-handling/utils.js
@@ -185,11 +185,17 @@ function cleanNodeList( nodeList, doc, schema, inline ) {
( ! schema[ tag ].isMatch || schema[ tag ].isMatch( node ) )
) {
if ( node.nodeType === ELEMENT_NODE ) {
- const { attributes = [], classes = [], children, require = [] } = schema[ tag ];
+ const {
+ attributes = [],
+ classes = [],
+ children,
+ require = [],
+ allowEmpty,
+ } = schema[ tag ];
// If the node is empty and it's supposed to have children,
// remove the node.
- if ( isEmpty( node ) && children ) {
+ if ( children && ! allowEmpty && isEmpty( node ) ) {
remove( node );
return;
}
diff --git a/test/integration/fixtures/markdown-in.txt b/test/integration/fixtures/markdown-in.txt
index c985608462247..b636a78143800 100644
--- a/test/integration/fixtures/markdown-in.txt
+++ b/test/integration/fixtures/markdown-in.txt
@@ -23,6 +23,10 @@ First Header | Second Header
Content from cell 1 | Content from cell 2
Content in the first column | Content in the second column
+| | |
+|---|---|
+| | Table with empty cells. |
+
## Quote
> First
diff --git a/test/integration/fixtures/markdown-out.html b/test/integration/fixtures/markdown-out.html
index cf83cf40050c4..ba9a33b7172cc 100644
--- a/test/integration/fixtures/markdown-out.html
+++ b/test/integration/fixtures/markdown-out.html
@@ -31,6 +31,10 @@ Table
First Header Second Header Content from cell 1 Content from cell 2 Content in the first column Content in the second column
+
+
+
+
Quote
From 62f81e10f92a1ec2bdd71c4fe0e249c1e5cb8ccd Mon Sep 17 00:00:00 2001
From: Marcus Kazmierczak
Date: Fri, 1 Mar 2019 11:14:45 -0800
Subject: [PATCH 055/169] Update Codeowners, add mkaz to docgen (#14198)
---
.github/CODEOWNERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index ce69bf49c1b59..5a0454ca08e56 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -28,7 +28,7 @@
/packages/babel-preset-default @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @nosolosw
/packages/browserslist-config @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
/packages/custom-templated-path-webpack-plugin @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
-/packages/docgen @nosolosw
+/packages/docgen @nosolosw @mkaz
/packages/e2e-test-utils @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra
/packages/e2e-tests @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @talldan
/packages/eslint-plugin @youknowriad @gziolo @aduth @ntwb @nerrad @ajitbohra @nosolosw
From d62a1282672cfe665feab26d500c166815a716e2 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Fri, 1 Mar 2019 15:54:23 -0500
Subject: [PATCH 056/169] Plugin: Remove wp-editor-font stylesheet override
(#14176)
---
lib/client-assets.php | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/lib/client-assets.php b/lib/client-assets.php
index dc9fff22a8386..9bd7cdb8be025 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -200,27 +200,6 @@ function gutenberg_register_scripts_and_styles() {
// Editor Styles.
// This empty stylesheet is defined to ensure backward compatibility.
gutenberg_override_style( 'wp-blocks', false );
- $fonts_url = '';
-
- /*
- * Translators: Use this to specify the proper Google Font name and variants
- * to load that is supported by your language. Do not translate.
- * Set to 'off' to disable loading.
- */
- $font_family = _x( 'Noto Serif:400,400i,700,700i', 'Google Font Name and Variants', 'gutenberg' );
- if ( 'off' !== $font_family ) {
- $query_args = array(
- 'family' => urlencode( $font_family ),
- );
- $fonts_url = esc_url_raw( add_query_arg( $query_args, 'https://fonts.googleapis.com/css' ) );
- }
-
- gutenberg_override_style(
- 'wp-editor-font',
- $fonts_url,
- array(),
- null
- );
gutenberg_override_style(
'wp-editor',
From 33253abc1a3e7bbd2e23fb6f69cfdf57846cc2db Mon Sep 17 00:00:00 2001
From: Pascal Birchler
Date: Fri, 1 Mar 2019 22:08:52 +0100
Subject: [PATCH 057/169] Bring i18n functionality up to date (#12559)
* Plugin: Add Text Domain to plugin headers
* Bring i18n functionality up to date
* Plugin: Set Gutenberg script translations as default domain
Filter loading behavior to load from plugin translation files
* Testing: Update gutenberg_override_script per localization changes
* Plugin: Provide second argument for set script translations
Technically optional, only after WP5.1+, which is newer than the current minimum version supported by Gutenberg
---
.gitignore | 2 -
babel.config.js | 12 ---
bin/build-plugin-zip.sh | 4 -
.../backward-compatibility/deprecations.md | 6 ++
gutenberg.php | 1 +
languages/README.md | 10 ---
lib/client-assets.php | 74 +++++++++++++++++--
lib/i18n.php | 11 ++-
phpcs.xml.dist | 1 -
phpunit/class-i18n-functions-test.php | 32 --------
phpunit/class-override-script-test.php | 21 +++++-
11 files changed, 97 insertions(+), 77 deletions(-)
delete mode 100644 languages/README.md
delete mode 100644 phpunit/class-i18n-functions-test.php
diff --git a/.gitignore b/.gitignore
index 5ba04948b2faf..2fb814f9616e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,6 @@ build-module
build-style
node_modules
gutenberg.zip
-languages/gutenberg.pot
-/languages/gutenberg-translations.php
# Directories/files that may appear in your environment
.DS_Store
diff --git a/babel.config.js b/babel.config.js
index 4dc16df8337b2..b56ad5b149478 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -3,17 +3,5 @@ module.exports = function( api ) {
return {
presets: [ '@wordpress/babel-preset-default' ],
- env: {
- production: {
- plugins: [
- [
- '@wordpress/babel-plugin-makepot',
- {
- output: 'languages/gutenberg.pot',
- },
- ],
- ],
- },
- },
};
};
diff --git a/bin/build-plugin-zip.sh b/bin/build-plugin-zip.sh
index a95f2ef74b23d..1a6344fa2e67a 100755
--- a/bin/build-plugin-zip.sh
+++ b/bin/build-plugin-zip.sh
@@ -97,8 +97,6 @@ status "Installing dependencies... 📦"
npm install
status "Generating build... 👷♀️"
npm run build
-status "Generating PHP file for wordpress.org to parse translations... 👷♂️"
-npx pot-to-php ./languages/gutenberg.pot ./languages/gutenberg-translations.php gutenberg
# Temporarily modify `gutenberg.php` with production constants defined. Use a
# temp file because `bin/generate-gutenberg-php.php` reads from `gutenberg.php`
@@ -118,8 +116,6 @@ zip -r gutenberg.zip \
post-content.php \
$vendor_scripts \
$build_files \
- languages/gutenberg.pot \
- languages/gutenberg-translations.php \
README.md
# Reset `gutenberg.php`.
diff --git a/docs/designers-developers/developers/backward-compatibility/deprecations.md b/docs/designers-developers/developers/backward-compatibility/deprecations.md
index 966b33d77cafe..8e8f8ee2d3c37 100644
--- a/docs/designers-developers/developers/backward-compatibility/deprecations.md
+++ b/docs/designers-developers/developers/backward-compatibility/deprecations.md
@@ -2,6 +2,12 @@
The Gutenberg project's deprecation policy is intended to support backward compatibility for releases, when possible. The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version.
+## 5.4.0
+
+- The PHP function `gutenberg_load_plugin_textdomain` has been removed.
+- The PHP function `gutenberg_get_jed_locale_data` has been removed.
+- The PHP function `gutenberg_load_locale_data` has been removed.
+
## 5.3.0
- The PHP function `gutenberg_redirect_to_classic_editor_when_saving_posts` has been removed.
diff --git a/gutenberg.php b/gutenberg.php
index db7f70d1cab95..993f9e4566834 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -5,6 +5,7 @@
* Description: Printing since 1440. This is the development plugin for the new block editor in core.
* Version: 5.1.1
* Author: Gutenberg Team
+ * Text Domain: gutenberg
*
* @package gutenberg
*/
diff --git a/languages/README.md b/languages/README.md
deleted file mode 100644
index 3f3ba1d4478c6..0000000000000
--- a/languages/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-Languages
-=========
-
-The generated POT template file is not included in this repository. To create this file locally, follow instructions from [CONTRIBUTING.md](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md) to install the project, then run the following command:
-
-```
-npm run build
-```
-
-After the build completes, you'll find a `gutenberg.pot` strings file in this directory.
diff --git a/lib/client-assets.php b/lib/client-assets.php
index 9bd7cdb8be025..040557e3cfb11 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -83,8 +83,71 @@ function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false
} else {
wp_register_script( $handle, $src, $deps, $ver, $in_footer );
}
+
+ /*
+ * `WP_Dependencies::set_translations` will fall over on itself if setting
+ * translations on the `wp-i18n` handle, since it internally adds `wp-i18n`
+ * as a dependency of itself, exhausting memory. The same applies for the
+ * polyfill script, which is a dependency _of_ `wp-i18n`.
+ *
+ * See: https://core.trac.wordpress.org/ticket/46089
+ */
+ if ( 'wp-i18n' !== $handle && 'wp-polyfill' !== $handle ) {
+ wp_set_script_translations( $handle, 'default' );
+ }
}
+/**
+ * Filters the default translation file load behavior to load the Gutenberg
+ * plugin translation file, if available.
+ *
+ * @param string|false $file Path to the translation file to load. False if
+ * there isn't one.
+ * @param string $handle Name of the script to register a translation
+ * domain to.
+ *
+ * @return string|false Filtered path to the Gutenberg translation file, if
+ * available.
+ */
+function gutenberg_override_translation_file( $file, $handle ) {
+ if ( ! $file ) {
+ return $file;
+ }
+
+ // Only override script handles generated from the Gutenberg plugin.
+ $packages_dependencies = include dirname( __FILE__ ) . '/packages-dependencies.php';
+ if ( ! isset( $packages_dependencies[ $handle ] ) ) {
+ return $file;
+ }
+
+ /*
+ * The default file will be in the plugins language directory, omitting the
+ * domain since Gutenberg assigns the script translations as the default.
+ *
+ * Example: /www/wp-content/languages/plugins/de_DE-07d88e6a803e01276b9bfcc1203e862e.json
+ *
+ * The logic of `load_script_textdomain` is such that it will assume to
+ * search in the plugins language directory, since the assigned source of
+ * the overridden Gutenberg script originates in the plugins directory.
+ *
+ * The plugin translation files each begin with the slug of the plugin, so
+ * it's a simple matter of prepending the Gutenberg plugin slug.
+ */
+ $path_parts = pathinfo( $file );
+ $plugin_translation_file = (
+ $path_parts['dirname'] .
+ '/gutenberg-' .
+ $path_parts['basename']
+ );
+
+ if ( ! is_readable( $plugin_translation_file ) ) {
+ return $file;
+ }
+
+ return $plugin_translation_file;
+}
+add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 );
+
/**
* Registers a style according to `wp_register_style`. Honors this request by
* deregistering any style by the same handler before registration.
@@ -485,14 +548,11 @@ function gutenberg_get_autosave_newer_than_post_save( $post ) {
/**
* Loads Gutenberg Locale Data.
+ *
+ * @deprecated 5.2.0
*/
function gutenberg_load_locale_data() {
- // Prepare Jed locale data.
- $locale_data = gutenberg_get_jed_locale_data( 'gutenberg' );
- wp_add_inline_script(
- 'wp-i18n',
- 'wp.i18n.setLocaleData( ' . json_encode( $locale_data ) . ' );'
- );
+ _deprecated_function( __FUNCTION__, '5.2.0' );
}
/**
@@ -680,8 +740,6 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
$initial_edits = null;
}
- gutenberg_load_locale_data();
-
// Preload server-registered block schemas.
wp_add_inline_script(
'wp-blocks',
diff --git a/lib/i18n.php b/lib/i18n.php
index ab347c8334285..72aadb0598f27 100644
--- a/lib/i18n.php
+++ b/lib/i18n.php
@@ -13,12 +13,15 @@
* Returns Jed-formatted localization data.
*
* @since 0.1.0
+ * @deprecated 5.2.0
*
* @param string $domain Translation domain.
*
* @return array
*/
function gutenberg_get_jed_locale_data( $domain ) {
+ _deprecated_function( __FUNCTION__, '5.2.0' );
+
$translations = get_translations_for_domain( $domain );
$locale = array(
@@ -43,12 +46,8 @@ function gutenberg_get_jed_locale_data( $domain ) {
* Load plugin text domain for translations.
*
* @since 0.1.0
+ * @deprecated 5.2.0
*/
function gutenberg_load_plugin_textdomain() {
- load_plugin_textdomain(
- 'gutenberg',
- false,
- plugin_basename( gutenberg_dir_path() ) . '/languages/'
- );
+ _deprecated_function( __FUNCTION__, '5.2.0' );
}
-add_action( 'plugins_loaded', 'gutenberg_load_plugin_textdomain' );
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 85a39fbd82a4a..e328ac8dde3fc 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -32,7 +32,6 @@
./vendor
- ./languages/gutenberg-translations.php
./packages/block-serialization-spec-parser/parser.php
diff --git a/phpunit/class-i18n-functions-test.php b/phpunit/class-i18n-functions-test.php
deleted file mode 100644
index db8abe35bb7a7..0000000000000
--- a/phpunit/class-i18n-functions-test.php
+++ /dev/null
@@ -1,32 +0,0 @@
-markTestSkipped( 'The path is not matching in docker test environment. Needs further investigation.' );
- return;
- }
-
- add_action( 'load_textdomain', array( $this, 'check_arguments_in_load_textdomaincheck' ), 10, 2 );
- gutenberg_load_plugin_textdomain();
- }
-
- public function check_arguments_in_load_textdomaincheck( $domain, $mofile ) {
- $content_language_file_pattern = 'languages[\\/]plugins[\\/]gutenberg-[a-z]{2}_[A-Z]{2}.mo$';
- $plugin_language_file_pattern = 'gutenberg[\\/]languages[\\/]gutenberg-[a-z]{2}_[A-Z]{2}.mo$';
- $regex_pattern = '#' . $content_language_file_pattern . '|' . $plugin_language_file_pattern . '#';
-
- $this->assertEquals( 'gutenberg', $domain );
- $this->assertRegExp( $regex_pattern, $mofile );
- }
-}
diff --git a/phpunit/class-override-script-test.php b/phpunit/class-override-script-test.php
index af9e5d73fff19..034fa2f25a618 100644
--- a/phpunit/class-override-script-test.php
+++ b/phpunit/class-override-script-test.php
@@ -24,6 +24,23 @@ function tearDown() {
wp_deregister_script( 'gutenberg-dummy-script' );
}
+ /**
+ * Tests that script is localized.
+ */
+ function test_localizes_script() {
+ gutenberg_override_script(
+ 'gutenberg-dummy-script',
+ 'https://example.com/',
+ array( 'dependency' ),
+ 'version',
+ false
+ );
+
+ global $wp_scripts;
+ $script = $wp_scripts->query( 'gutenberg-dummy-script', 'registered' );
+ $this->assertEquals( array( 'dependency', 'wp-i18n' ), $script->deps );
+ }
+
/**
* Tests that script properties are overridden.
*/
@@ -39,7 +56,7 @@ function test_replaces_registered_properties() {
global $wp_scripts;
$script = $wp_scripts->query( 'gutenberg-dummy-script', 'registered' );
$this->assertEquals( 'https://example.com/updated', $script->src );
- $this->assertEquals( array( 'updated-dependency' ), $script->deps );
+ $this->assertEquals( array( 'updated-dependency', 'wp-i18n' ), $script->deps );
$this->assertEquals( 'updated-version', $script->ver );
$this->assertEquals( 1, $script->extra['group'] );
}
@@ -59,7 +76,7 @@ function test_registers_new_script() {
global $wp_scripts;
$script = $wp_scripts->query( 'gutenberg-second-dummy-script', 'registered' );
$this->assertEquals( 'https://example.com/', $script->src );
- $this->assertEquals( array( 'dependency' ), $script->deps );
+ $this->assertEquals( array( 'dependency', 'wp-i18n' ), $script->deps );
$this->assertEquals( 'version', $script->ver );
$this->assertEquals( 1, $script->extra['group'] );
}
From 8bd00c40e40f33829f488e17c323067292835024 Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Fri, 1 Mar 2019 22:04:11 +0000
Subject: [PATCH 058/169] Fix: deleting the last block triggers a focus loss.
(#14189)
## Description
This PR fixes a problem: if the post contains one block and it is removed the focus is lost.
This is a regression that happened when removeBlocks action was refactored to be a generator. We had an effect that inserts the default block during remove blocks action when certain conditions are met, this effect stopped working.
This PR removes the effect and makes sure everything is handled by the removeBlocks action creator.
End to end test available at https://github.com/WordPress/gutenberg/pull/14191.
## How has this been tested?
I created a new post.
I wrote something in a paragraph I removed the paragraph using the remove button in the side menu and I verified the default block was added.
---
packages/block-editor/src/store/actions.js | 11 +++++++++++
packages/block-editor/src/store/effects.js | 3 ---
packages/block-editor/src/store/test/actions.js | 15 ++++++++++++++-
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
index a94a526f4a5da..07f150f53d21a 100644
--- a/packages/block-editor/src/store/actions.js
+++ b/packages/block-editor/src/store/actions.js
@@ -393,6 +393,17 @@ export function* removeBlocks( clientIds, selectPrevious = true ) {
type: 'REMOVE_BLOCKS',
clientIds,
};
+
+ const count = yield select(
+ 'core/block-editor',
+ 'getBlockCount',
+ );
+
+ // To avoid a focus loss when removing the last block, assure there is
+ // always a default block if the last of the blocks have been removed.
+ if ( count === 0 ) {
+ yield insertDefaultBlock();
+ }
}
/**
diff --git a/packages/block-editor/src/store/effects.js b/packages/block-editor/src/store/effects.js
index f02ef6fbd7231..ca46e1afb8f4a 100644
--- a/packages/block-editor/src/store/effects.js
+++ b/packages/block-editor/src/store/effects.js
@@ -127,9 +127,6 @@ export default {
RESET_BLOCKS: [
validateBlocksToTemplate,
],
- REMOVE_BLOCKS: [
- ensureDefaultBlock,
- ],
REPLACE_BLOCKS: [
ensureDefaultBlock,
],
diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js
index 3ae9039505356..2ca6839dc57e2 100644
--- a/packages/block-editor/src/store/test/actions.js
+++ b/packages/block-editor/src/store/test/actions.js
@@ -28,6 +28,7 @@ import {
toggleBlockMode,
updateBlockListSettings,
} from '../actions';
+import { select } from '../controls';
describe( 'actions', () => {
describe( 'resetBlocks', () => {
@@ -218,6 +219,10 @@ describe( 'actions', () => {
type: 'REMOVE_BLOCKS',
clientIds,
},
+ select(
+ 'core/block-editor',
+ 'getBlockCount',
+ ),
] );
} );
} );
@@ -234,10 +239,14 @@ describe( 'actions', () => {
type: 'REMOVE_BLOCKS',
clientIds: [ clientId ],
},
+ select(
+ 'core/block-editor',
+ 'getBlockCount',
+ ),
] );
} );
- it( 'should return REMOVE_BLOCKS action, opting out of remove previous', () => {
+ it( 'should return REMOVE_BLOCKS action, opting out of select previous', () => {
const clientId = 'myclientid';
const actions = Array.from( removeBlock( clientId, false ) );
@@ -247,6 +256,10 @@ describe( 'actions', () => {
type: 'REMOVE_BLOCKS',
clientIds: [ clientId ],
},
+ select(
+ 'core/block-editor',
+ 'getBlockCount',
+ ),
] );
} );
} );
From c29b729a6d107fc5a7ef53efafda126d6fbb68e0 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Mon, 4 Mar 2019 07:42:58 +0100
Subject: [PATCH 059/169] Clarify the block editor settings from the post
editor settings (#14082)
---
.../developers/data/data-core-block-editor.md | 6 ++--
.../developers/data/data-core-editor.md | 22 +++++++++++++-
.../src/components/provider/index.js | 10 +++----
packages/block-editor/src/index.js | 2 ++
packages/block-editor/src/store/actions.js | 6 ++--
packages/block-editor/src/store/defaults.js | 28 +++++++++---------
packages/block-editor/src/store/reducer.js | 6 ++--
packages/block-editor/src/store/selectors.js | 4 +--
.../block-editor/src/store/test/effects.js | 8 ++---
packages/block-library/src/html/edit.js | 4 +--
packages/block-library/src/image/edit.js | 4 +--
packages/block-library/src/paragraph/edit.js | 4 +--
packages/block-library/src/pullquote/index.js | 2 +-
.../components/header/header-toolbar/index.js | 2 +-
.../components/header/mode-switcher/index.js | 2 +-
.../components/keyboard-shortcuts/index.js | 2 +-
.../edit-post/src/components/layout/index.js | 2 +-
.../options-modal/meta-boxes-section.js | 2 +-
.../options/enable-custom-fields.js | 2 +-
.../src/components/text-editor/index.js | 2 +-
.../src/components/alignment-toolbar/index.js | 4 +--
.../src/components/autosave-monitor/index.js | 3 +-
.../block-alignment-toolbar/index.js | 7 +++--
.../editor/src/components/block-list/block.js | 8 ++---
.../src/components/block-list/hover-area.js | 2 +-
.../color-palette/with-color-context.js | 2 +-
.../src/components/colors/with-colors.js | 2 +-
.../default-block-appender/index.js | 4 +--
.../default-block-appender/index.native.js | 4 +--
.../components/font-sizes/font-size-picker.js | 2 +-
.../components/font-sizes/with-font-sizes.js | 2 +-
.../src/components/media-placeholder/index.js | 4 +--
.../src/components/page-attributes/check.js | 5 +---
.../components/page-attributes/template.js | 3 +-
.../src/components/post-format/check.js | 3 +-
.../src/components/post-locked-modal/index.js | 5 +---
.../editor/src/components/post-title/index.js | 4 +--
.../editor/src/components/provider/index.js | 29 +++++++++++++++++--
packages/editor/src/hooks/align.js | 4 +--
packages/editor/src/store/actions.js | 15 +++++++++-
packages/editor/src/store/defaults.js | 24 +++++++++++++++
packages/editor/src/store/reducer.js | 22 ++++++++++++++
packages/editor/src/store/selectors.js | 12 +++++++-
.../editor/src/utils/media-upload/index.js | 5 +---
44 files changed, 201 insertions(+), 94 deletions(-)
diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md
index 3166400bd3fbf..03929e3671685 100644
--- a/docs/designers-developers/developers/data/data-core-block-editor.md
+++ b/docs/designers-developers/developers/data/data-core-block-editor.md
@@ -737,7 +737,7 @@ Returns the Block List settings of a block, if any exist.
Block settings of the block if set.
-### getEditorSettings
+### getSettings
Returns the editor settings.
@@ -1027,9 +1027,9 @@ Returns an action object that changes the nested settings of a given block.
being received.
* settings: Object with the new settings for the nested block.
-### updateEditorSettings
+### updateSettings
-Returns an action object used in signalling that the editor settings have been updated.
+Returns an action object used in signalling that the block editor settings have been updated.
*Parameters*
diff --git a/docs/designers-developers/developers/data/data-core-editor.md b/docs/designers-developers/developers/data/data-core-editor.md
index 3a22e11c90121..cd251dc92d678 100644
--- a/docs/designers-developers/developers/data/data-core-editor.md
+++ b/docs/designers-developers/developers/data/data-core-editor.md
@@ -718,6 +718,18 @@ Is the editor ready
is Ready.
+### getEditorSettings
+
+Returns the post editor settings.
+
+*Parameters*
+
+ * state: Editor state.
+
+*Returns*
+
+The editor settings object.
+
## Actions
### setupEditor
@@ -967,4 +979,12 @@ Returns an action object used to signal that the blocks have been updated.
*Parameters*
* blocks: Block Array.
- * options: Optional options.
\ No newline at end of file
+ * options: Optional options.
+
+### updateEditorSettings
+
+Returns an action object used in signalling that the post editor settings have been updated.
+
+*Parameters*
+
+ * settings: Updated settings
\ No newline at end of file
diff --git a/packages/block-editor/src/components/provider/index.js b/packages/block-editor/src/components/provider/index.js
index a1d3063962b5d..a8073ace46c5d 100644
--- a/packages/block-editor/src/components/provider/index.js
+++ b/packages/block-editor/src/components/provider/index.js
@@ -30,7 +30,7 @@ const withRegistry = createHigherOrderComponent(
class BlockEditorProvider extends Component {
componentDidMount() {
- this.props.updateEditorSettings( this.props.settings );
+ this.props.updateSettings( this.props.settings );
this.props.resetBlocks( this.props.value );
this.attachChangeObserver( this.props.registry );
}
@@ -38,14 +38,14 @@ class BlockEditorProvider extends Component {
componentDidUpdate( prevProps ) {
const {
settings,
- updateEditorSettings,
+ updateSettings,
value,
resetBlocks,
registry,
} = this.props;
if ( settings !== prevProps.settings ) {
- updateEditorSettings( settings );
+ updateSettings( settings );
}
if ( registry !== prevProps.registry ) {
@@ -139,12 +139,12 @@ class BlockEditorProvider extends Component {
export default compose( [
withDispatch( ( dispatch ) => {
const {
- updateEditorSettings,
+ updateSettings,
resetBlocks,
} = dispatch( 'core/block-editor' );
return {
- updateEditorSettings,
+ updateSettings,
resetBlocks,
};
} ),
diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js
index 9421db61f16e9..58e920befd5ea 100644
--- a/packages/block-editor/src/index.js
+++ b/packages/block-editor/src/index.js
@@ -9,3 +9,5 @@ import '@wordpress/blocks';
import './store';
export * from './components';
+
+export { SETTINGS_DEFAULTS } from './store/defaults';
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
index 07f150f53d21a..a2a1bc6e6e628 100644
--- a/packages/block-editor/src/store/actions.js
+++ b/packages/block-editor/src/store/actions.js
@@ -514,15 +514,15 @@ export function updateBlockListSettings( clientId, settings ) {
}
/*
- * Returns an action object used in signalling that the editor settings have been updated.
+ * Returns an action object used in signalling that the block editor settings have been updated.
*
* @param {Object} settings Updated settings
*
* @return {Object} Action object
*/
-export function updateEditorSettings( settings ) {
+export function updateSettings( settings ) {
return {
- type: 'UPDATE_EDITOR_SETTINGS',
+ type: 'UPDATE_SETTINGS',
settings,
};
}
diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js
index 31d1574d6283a..3e61e459c53f6 100644
--- a/packages/block-editor/src/store/defaults.js
+++ b/packages/block-editor/src/store/defaults.js
@@ -10,17 +10,22 @@ export const PREFERENCES_DEFAULTS = {
/**
* The default editor settings
*
- * alignWide boolean Enable/Disable Wide/Full Alignments
- * colors Array Palette colors
- * fontSizes Array Available font sizes
- * imageSizes Array Available image sizes
- * maxWidth number Max width to constraint resizing
- * blockTypes boolean|Array Allowed block types
- * hasFixedToolbar boolean Whether or not the editor toolbar is fixed
- * focusMode boolean Whether the focus mode is enabled or not
- * richEditingEnabled boolean Whether rich editing is enabled or not
+ * alignWide boolean Enable/Disable Wide/Full Alignments
+ * colors Array Palette colors
+ * disableCustomColors boolean Whether or not the custom colors are disabled
+ * fontSizes Array Available font sizes
+ * disableCustomFontSizes boolean Whether or not the custom font sizes are disabled
+ * imageSizes Array Available image sizes
+ * maxWidth number Max width to constraint resizing
+ * blockTypes boolean|Array Allowed block types
+ * hasFixedToolbar boolean Whether or not the editor toolbar is fixed
+ * focusMode boolean Whether the focus mode is enabled or not
+ * styles Array Editor Styles
+ * isRTL boolean Whether the editor is in RTL mode
+ * bodyPlaceholder string Empty post placeholder
+ * titlePlaceholder string Empty title placeholder
*/
-export const EDITOR_SETTINGS_DEFAULTS = {
+export const SETTINGS_DEFAULTS = {
alignWide: false,
colors: [
{
@@ -126,8 +131,5 @@ export const EDITOR_SETTINGS_DEFAULTS = {
// List of allowed mime types and file extensions.
allowedMimeTypes: null,
-
- // Whether richs editing is enabled or not.
- richEditingEnabled: true,
};
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
index bcd8b3295c4bc..007c5f4a8f6d4 100644
--- a/packages/block-editor/src/store/reducer.js
+++ b/packages/block-editor/src/store/reducer.js
@@ -26,7 +26,7 @@ import { isReusableBlock } from '@wordpress/blocks';
*/
import {
PREFERENCES_DEFAULTS,
- EDITOR_SETTINGS_DEFAULTS,
+ SETTINGS_DEFAULTS,
} from './defaults';
import { insertAt, moveTo } from './array';
@@ -830,9 +830,9 @@ export function template( state = { isValid: true }, action ) {
*
* @return {Object} Updated state.
*/
-export function settings( state = EDITOR_SETTINGS_DEFAULTS, action ) {
+export function settings( state = SETTINGS_DEFAULTS, action ) {
switch ( action.type ) {
- case 'UPDATE_EDITOR_SETTINGS':
+ case 'UPDATE_SETTINGS':
return {
...state,
...action.settings,
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index efadb31bf0ff2..d1b142530cff6 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -1034,7 +1034,7 @@ const canInsertBlockTypeUnmemoized = ( state, blockName, rootClientId = null ) =
return false;
}
- const { allowedBlockTypes } = getEditorSettings( state );
+ const { allowedBlockTypes } = getSettings( state );
const isBlockAllowedInEditor = checkAllowList( allowedBlockTypes, blockName, true );
if ( ! isBlockAllowedInEditor ) {
@@ -1350,7 +1350,7 @@ export function getBlockListSettings( state, clientId ) {
*
* @return {Object} The editor settings object.
*/
-export function getEditorSettings( state ) {
+export function getSettings( state ) {
return state.settings;
}
diff --git a/packages/block-editor/src/store/test/effects.js b/packages/block-editor/src/store/test/effects.js
index 090779cbad0d9..34300ed6d42ff 100644
--- a/packages/block-editor/src/store/test/effects.js
+++ b/packages/block-editor/src/store/test/effects.js
@@ -18,7 +18,7 @@ import { createRegistry } from '@wordpress/data';
* Internal dependencies
*/
import actions, {
- updateEditorSettings,
+ updateSettings,
mergeBlocks,
replaceBlocks,
resetBlocks,
@@ -234,7 +234,7 @@ describe( 'effects', () => {
} );
it( 'should return undefined if invalid but unlocked', () => {
- store.dispatch( updateEditorSettings( {
+ store.dispatch( updateSettings( {
template: [
[ 'core/foo', {} ],
],
@@ -248,7 +248,7 @@ describe( 'effects', () => {
} );
it( 'should return undefined if locked and valid', () => {
- store.dispatch( updateEditorSettings( {
+ store.dispatch( updateSettings( {
template: [
[ 'core/test-block' ],
],
@@ -263,7 +263,7 @@ describe( 'effects', () => {
} );
it( 'should return validity set action if invalid on default state', () => {
- store.dispatch( updateEditorSettings( {
+ store.dispatch( updateSettings( {
template: [
[ 'core/foo' ],
],
diff --git a/packages/block-library/src/html/edit.js b/packages/block-library/src/html/edit.js
index 6dd2bb6f1fd92..0000654785a43 100644
--- a/packages/block-library/src/html/edit.js
+++ b/packages/block-library/src/html/edit.js
@@ -87,8 +87,8 @@ class HTMLEdit extends Component {
}
}
export default withSelect( ( select ) => {
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getSettings } = select( 'core/block-editor' );
return {
- styles: getEditorSettings().styles,
+ styles: getSettings().styles,
};
} )( HTMLEdit );
diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js
index deaeaf79766dc..f8308165923f1 100644
--- a/packages/block-library/src/image/edit.js
+++ b/packages/block-library/src/image/edit.js
@@ -705,9 +705,9 @@ class ImageEdit extends Component {
export default compose( [
withSelect( ( select, props ) => {
const { getMedia } = select( 'core' );
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getSettings } = select( 'core/block-editor' );
const { id } = props.attributes;
- const { maxWidth, isRTL, imageSizes } = getEditorSettings();
+ const { maxWidth, isRTL, imageSizes } = getSettings();
return {
image: id ? getMedia( id ) : null,
diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js
index 2aaad4c8060ff..060080b971e59 100644
--- a/packages/block-library/src/paragraph/edit.js
+++ b/packages/block-library/src/paragraph/edit.js
@@ -259,10 +259,10 @@ const ParagraphEdit = compose( [
withFontSizes( 'fontSize' ),
applyFallbackStyles,
withSelect( ( select ) => {
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getSettings } = select( 'core/block-editor' );
return {
- isRTL: getEditorSettings().isRTL,
+ isRTL: getSettings().isRTL,
};
} ),
] )( ParagraphBlock );
diff --git a/packages/block-library/src/pullquote/index.js b/packages/block-library/src/pullquote/index.js
index beaf1d9549aaa..766cb78861158 100644
--- a/packages/block-library/src/pullquote/index.js
+++ b/packages/block-library/src/pullquote/index.js
@@ -100,7 +100,7 @@ export const settings = {
// Is normal style and a named color is being used, we need to retrieve the color value to set the style,
// as there is no expectation that themes create classes that set border colors.
} else if ( mainColor ) {
- const colors = get( select( 'core/block-editor' ).getEditorSettings(), [ 'colors' ], [] );
+ const colors = get( select( 'core/block-editor' ).getSettings(), [ 'colors' ], [] );
const colorObject = getColorObjectByAttributeValues( colors, mainColor );
figureStyles = {
borderColor: colorObject.color,
diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js
index 7740b3b157a99..7d99c1f889e04 100644
--- a/packages/edit-post/src/components/header/header-toolbar/index.js
+++ b/packages/edit-post/src/components/header/header-toolbar/index.js
@@ -57,7 +57,7 @@ export default compose( [
withSelect( ( select ) => ( {
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
// This setting (richEditingEnabled) should not live in the block editor's setting.
- showInserter: select( 'core/edit-post' ).getEditorMode() === 'visual' && select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
+ showInserter: select( 'core/edit-post' ).getEditorMode() === 'visual' && select( 'core/editor' ).getEditorSettings().richEditingEnabled,
isTextModeEnabled: select( 'core/edit-post' ).getEditorMode() === 'text',
} ) ),
withViewportMatch( { isLargeViewport: 'medium' } ),
diff --git a/packages/edit-post/src/components/header/mode-switcher/index.js b/packages/edit-post/src/components/header/mode-switcher/index.js
index 058d9c74eeb8c..52ca0b78cfc94 100644
--- a/packages/edit-post/src/components/header/mode-switcher/index.js
+++ b/packages/edit-post/src/components/header/mode-switcher/index.js
@@ -49,7 +49,7 @@ function ModeSwitcher( { onSwitch, mode } ) {
export default compose( [
withSelect( ( select ) => ( {
- isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
mode: select( 'core/edit-post' ).getEditorMode(),
} ) ),
ifCondition( ( { isRichEditingEnabled } ) => isRichEditingEnabled ),
diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js
index b5c09178efd68..bde16d7431106 100644
--- a/packages/edit-post/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js
@@ -55,7 +55,7 @@ class EditorModeKeyboardShortcuts extends Component {
export default compose( [
withSelect( ( select ) => ( {
- isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
mode: select( 'core/edit-post' ).getEditorMode(),
isEditorSidebarOpen: select( 'core/edit-post' ).isEditorSidebarOpened(),
} ) ),
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js
index 76c10e8a78032..f0876f4672287 100644
--- a/packages/edit-post/src/components/layout/index.js
+++ b/packages/edit-post/src/components/layout/index.js
@@ -137,7 +137,7 @@ export default compose(
hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ),
hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(),
isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(),
- isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
} ) ),
withDispatch( ( dispatch ) => {
const { closePublishSidebar, togglePublishSidebar } = dispatch( 'core/edit-post' );
diff --git a/packages/edit-post/src/components/options-modal/meta-boxes-section.js b/packages/edit-post/src/components/options-modal/meta-boxes-section.js
index a5b8e0e85ad50..e4ab459a0e41a 100644
--- a/packages/edit-post/src/components/options-modal/meta-boxes-section.js
+++ b/packages/edit-post/src/components/options-modal/meta-boxes-section.js
@@ -34,7 +34,7 @@ export function MetaBoxesSection( { areCustomFieldsRegistered, metaBoxes, ...sec
}
export default withSelect( ( select ) => {
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getEditorSettings } = select( 'core/editor' );
const { getAllMetaBoxes } = select( 'core/edit-post' );
return {
diff --git a/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js b/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js
index e9a319efbebfe..140c6f1c46d2d 100644
--- a/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js
+++ b/packages/edit-post/src/components/options-modal/options/enable-custom-fields.js
@@ -43,5 +43,5 @@ export class EnableCustomFieldsOption extends Component {
}
export default withSelect( ( select ) => ( {
- isChecked: !! select( 'core/block-editor' ).getEditorSettings().enableCustomFields,
+ isChecked: !! select( 'core/editor' ).getEditorSettings().enableCustomFields,
} ) )( EnableCustomFieldsOption );
diff --git a/packages/edit-post/src/components/text-editor/index.js b/packages/edit-post/src/components/text-editor/index.js
index 0cebd6e7396ad..882e97dd7e83c 100644
--- a/packages/edit-post/src/components/text-editor/index.js
+++ b/packages/edit-post/src/components/text-editor/index.js
@@ -38,7 +38,7 @@ function TextEditor( { onExit, isRichEditingEnabled } ) {
export default compose(
withSelect( ( select ) => ( {
- isRichEditingEnabled: select( 'core/block-editor' ).getEditorSettings().richEditingEnabled,
+ isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
} ) ),
withDispatch( ( dispatch ) => {
return {
diff --git a/packages/editor/src/components/alignment-toolbar/index.js b/packages/editor/src/components/alignment-toolbar/index.js
index bf5fe27650e2a..1124f44f033e1 100644
--- a/packages/editor/src/components/alignment-toolbar/index.js
+++ b/packages/editor/src/components/alignment-toolbar/index.js
@@ -69,10 +69,10 @@ export default compose(
} ),
withViewportMatch( { isLargeViewport: 'medium' } ),
withSelect( ( select, { clientId, isLargeViewport, isCollapsed } ) => {
- const { getBlockRootClientId, getEditorSettings } = select( 'core/block-editor' );
+ const { getBlockRootClientId, getSettings } = select( 'core/block-editor' );
return {
isCollapsed: isCollapsed || ! isLargeViewport || (
- ! getEditorSettings().hasFixedToolbar &&
+ ! getSettings().hasFixedToolbar &&
getBlockRootClientId( clientId )
),
};
diff --git a/packages/editor/src/components/autosave-monitor/index.js b/packages/editor/src/components/autosave-monitor/index.js
index 23cff18b19411..73f15c285cbff 100644
--- a/packages/editor/src/components/autosave-monitor/index.js
+++ b/packages/editor/src/components/autosave-monitor/index.js
@@ -66,8 +66,7 @@ export default compose( [
isAutosavingPost,
} = select( 'core/editor' );
- // This settings should not live in the block editor.
- const { autosaveInterval } = select( 'core/block-editor' ).getEditorSettings();
+ const { autosaveInterval } = select( 'core/editor' ).getEditorSettings();
return {
isDirty: isEditedPostDirty(),
diff --git a/packages/editor/src/components/block-alignment-toolbar/index.js b/packages/editor/src/components/block-alignment-toolbar/index.js
index 0dcc4bda1412f..adf02cd90b08b 100644
--- a/packages/editor/src/components/block-alignment-toolbar/index.js
+++ b/packages/editor/src/components/block-alignment-toolbar/index.js
@@ -75,11 +75,12 @@ export default compose(
} ),
withViewportMatch( { isLargeViewport: 'medium' } ),
withSelect( ( select, { clientId, isLargeViewport, isCollapsed } ) => {
- const { getBlockRootClientId, getEditorSettings } = select( 'core/block-editor' );
+ const { getBlockRootClientId, getSettings } = select( 'core/block-editor' );
+ const settings = getSettings();
return {
- wideControlsEnabled: select( 'core/block-editor' ).getEditorSettings().alignWide,
+ wideControlsEnabled: settings.alignWide,
isCollapsed: isCollapsed || ! isLargeViewport || (
- ! getEditorSettings().hasFixedToolbar &&
+ ! settings.hasFixedToolbar &&
getBlockRootClientId( clientId )
),
};
diff --git a/packages/editor/src/components/block-list/block.js b/packages/editor/src/components/block-list/block.js
index 4a977903e44c7..2b828a34cdd93 100644
--- a/packages/editor/src/components/block-list/block.js
+++ b/packages/editor/src/components/block-list/block.js
@@ -633,14 +633,14 @@ const applyWithSelect = withSelect(
getBlockMode,
isSelectionEnabled,
getSelectedBlocksInitialCaretPosition,
- getEditorSettings,
+ getSettings,
hasSelectedInnerBlock,
getTemplateLock,
__unstableGetBlockWithoutInnerBlocks,
} = select( 'core/block-editor' );
const block = __unstableGetBlockWithoutInnerBlocks( clientId );
const isSelected = isBlockSelected( clientId );
- const { hasFixedToolbar, focusMode } = getEditorSettings();
+ const { hasFixedToolbar, focusMode } = getSettings();
const templateLock = getTemplateLock( rootClientId );
const isParentOfSelectedBlock = hasSelectedInnerBlock( clientId, true );
@@ -748,8 +748,8 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
replaceBlocks( [ ownProps.clientId ], blocks );
},
onMetaChange( updatedMeta ) {
- const { getEditorSettings } = select( 'core/block-editor' );
- const onChangeMeta = getEditorSettings().__experimentalMetaSource.onChange;
+ const { getSettings } = select( 'core/block-editor' );
+ const onChangeMeta = getSettings().__experimentalMetaSource.onChange;
onChangeMeta( updatedMeta );
},
onShiftSelection() {
diff --git a/packages/editor/src/components/block-list/hover-area.js b/packages/editor/src/components/block-list/hover-area.js
index d7091552b4885..a79b0bcd9b088 100644
--- a/packages/editor/src/components/block-list/hover-area.js
+++ b/packages/editor/src/components/block-list/hover-area.js
@@ -76,7 +76,7 @@ class HoverArea extends Component {
export default withSelect( ( select ) => {
return {
- isRTL: select( 'core/block-editor' ).getEditorSettings().isRTL,
+ isRTL: select( 'core/block-editor' ).getSettings().isRTL,
};
} )( HoverArea );
diff --git a/packages/editor/src/components/color-palette/with-color-context.js b/packages/editor/src/components/color-palette/with-color-context.js
index 48eaa8085fca9..1c32141f2a3f1 100644
--- a/packages/editor/src/components/color-palette/with-color-context.js
+++ b/packages/editor/src/components/color-palette/with-color-context.js
@@ -13,7 +13,7 @@ import { withSelect } from '@wordpress/data';
export default createHigherOrderComponent(
withSelect(
( select, ownProps ) => {
- const settings = select( 'core/block-editor' ).getEditorSettings();
+ const settings = select( 'core/block-editor' ).getSettings();
const colors = ownProps.colors === undefined ?
settings.colors : ownProps.colors;
diff --git a/packages/editor/src/components/colors/with-colors.js b/packages/editor/src/components/colors/with-colors.js
index 11524408b4a29..516a5a023414f 100644
--- a/packages/editor/src/components/colors/with-colors.js
+++ b/packages/editor/src/components/colors/with-colors.js
@@ -36,7 +36,7 @@ const withCustomColorPalette = ( colorsArray ) => createHigherOrderComponent( (
* @return {function} The higher order component.
*/
const withEditorColorPalette = () => withSelect( ( select ) => {
- const settings = select( 'core/block-editor' ).getEditorSettings();
+ const settings = select( 'core/block-editor' ).getSettings();
return {
colors: get( settings, [ 'colors' ], DEFAULT_COLORS ),
};
diff --git a/packages/editor/src/components/default-block-appender/index.js b/packages/editor/src/components/default-block-appender/index.js
index 5a320ba4c105d..183da30afd8b1 100644
--- a/packages/editor/src/components/default-block-appender/index.js
+++ b/packages/editor/src/components/default-block-appender/index.js
@@ -74,12 +74,12 @@ export function DefaultBlockAppender( {
export default compose(
withState( { hovered: false } ),
withSelect( ( select, ownProps ) => {
- const { getBlockCount, getBlockName, isBlockValid, getEditorSettings, getTemplateLock } = select( 'core/block-editor' );
+ const { getBlockCount, getBlockName, isBlockValid, getSettings, getTemplateLock } = select( 'core/block-editor' );
const isEmpty = ! getBlockCount( ownProps.rootClientId );
const isLastBlockDefault = getBlockName( ownProps.lastBlockClientId ) === getDefaultBlockName();
const isLastBlockValid = isBlockValid( ownProps.lastBlockClientId );
- const { bodyPlaceholder } = getEditorSettings();
+ const { bodyPlaceholder } = getSettings();
return {
isVisible: isEmpty || ! isLastBlockDefault || ! isLastBlockValid,
diff --git a/packages/editor/src/components/default-block-appender/index.native.js b/packages/editor/src/components/default-block-appender/index.native.js
index ab34b5ebbde21..eae900cc70362 100644
--- a/packages/editor/src/components/default-block-appender/index.native.js
+++ b/packages/editor/src/components/default-block-appender/index.native.js
@@ -49,10 +49,10 @@ export function DefaultBlockAppender( {
export default compose(
withSelect( ( select, ownProps ) => {
- const { getBlockCount, getEditorSettings, getTemplateLock } = select( 'core/block-editor' );
+ const { getBlockCount, getSettings, getTemplateLock } = select( 'core/block-editor' );
const isEmpty = ! getBlockCount( ownProps.rootClientId );
- const { bodyPlaceholder } = getEditorSettings();
+ const { bodyPlaceholder } = getSettings();
return {
isVisible: isEmpty,
diff --git a/packages/editor/src/components/font-sizes/font-size-picker.js b/packages/editor/src/components/font-sizes/font-size-picker.js
index 6989bb2233669..e2dbe3dba8134 100644
--- a/packages/editor/src/components/font-sizes/font-size-picker.js
+++ b/packages/editor/src/components/font-sizes/font-size-picker.js
@@ -9,7 +9,7 @@ export default withSelect(
const {
disableCustomFontSizes,
fontSizes,
- } = select( 'core/block-editor' ).getEditorSettings();
+ } = select( 'core/block-editor' ).getSettings();
return {
disableCustomFontSizes,
diff --git a/packages/editor/src/components/font-sizes/with-font-sizes.js b/packages/editor/src/components/font-sizes/with-font-sizes.js
index 39953baf801f2..23a8c76a11d25 100644
--- a/packages/editor/src/components/font-sizes/with-font-sizes.js
+++ b/packages/editor/src/components/font-sizes/with-font-sizes.js
@@ -38,7 +38,7 @@ export default ( ...fontSizeNames ) => {
return createHigherOrderComponent(
compose( [
withSelect( ( select ) => {
- const { fontSizes } = select( 'core/block-editor' ).getEditorSettings();
+ const { fontSizes } = select( 'core/block-editor' ).getSettings();
return {
fontSizes,
};
diff --git a/packages/editor/src/components/media-placeholder/index.js b/packages/editor/src/components/media-placeholder/index.js
index 33b2792beb65d..2270b87762ea3 100644
--- a/packages/editor/src/components/media-placeholder/index.js
+++ b/packages/editor/src/components/media-placeholder/index.js
@@ -263,11 +263,11 @@ export class MediaPlaceholder extends Component {
const applyWithSelect = withSelect( ( select ) => {
const { canUser } = select( 'core' );
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getSettings } = select( 'core/block-editor' );
return {
hasUploadPermissions: defaultTo( canUser( 'create', 'media' ), true ),
- mediaUpload: getEditorSettings().__experimentalMediaUpload,
+ mediaUpload: getSettings().__experimentalMediaUpload,
};
} );
diff --git a/packages/editor/src/components/page-attributes/check.js b/packages/editor/src/components/page-attributes/check.js
index 014b2aa29136a..56be444a04985 100644
--- a/packages/editor/src/components/page-attributes/check.js
+++ b/packages/editor/src/components/page-attributes/check.js
@@ -20,11 +20,8 @@ export function PageAttributesCheck( { availableTemplates, postType, children }
}
export default withSelect( ( select ) => {
- const { getEditedPostAttribute } = select( 'core/editor' );
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getEditedPostAttribute, getEditorSettings } = select( 'core/editor' );
const { getPostType } = select( 'core' );
-
- // This setting should not live in the block-editor module.
const { availableTemplates } = getEditorSettings();
return {
postType: getPostType( getEditedPostAttribute( 'type' ) ),
diff --git a/packages/editor/src/components/page-attributes/template.js b/packages/editor/src/components/page-attributes/template.js
index 8e6d9cef689cd..3646a05e3206f 100644
--- a/packages/editor/src/components/page-attributes/template.js
+++ b/packages/editor/src/components/page-attributes/template.js
@@ -33,8 +33,7 @@ export function PageTemplate( { availableTemplates, selectedTemplate, onUpdate }
export default compose(
withSelect( ( select ) => {
- const { getEditedPostAttribute } = select( 'core/editor' );
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getEditedPostAttribute, getEditorSettings } = select( 'core/editor' );
const { availableTemplates } = getEditorSettings();
return {
selectedTemplate: getEditedPostAttribute( 'template' ),
diff --git a/packages/editor/src/components/post-format/check.js b/packages/editor/src/components/post-format/check.js
index d13e354655e9d..cd124553a7cf1 100644
--- a/packages/editor/src/components/post-format/check.js
+++ b/packages/editor/src/components/post-format/check.js
@@ -15,8 +15,7 @@ function PostFormatCheck( { disablePostFormats, ...props } ) {
export default withSelect(
( select ) => {
- // This setting should not live in the block-editor's store.
- const editorSettings = select( 'core/block-editor' ).getEditorSettings();
+ const editorSettings = select( 'core/editor' ).getEditorSettings();
return {
disablePostFormats: editorSettings.disablePostFormats,
};
diff --git a/packages/editor/src/components/post-locked-modal/index.js b/packages/editor/src/components/post-locked-modal/index.js
index 8a9b88cbc2109..dd681f1f80980 100644
--- a/packages/editor/src/components/post-locked-modal/index.js
+++ b/packages/editor/src/components/post-locked-modal/index.js
@@ -220,17 +220,14 @@ export default compose(
getCurrentPostId,
getActivePostLock,
getEditedPostAttribute,
- } = select( 'core/editor' );
- const {
getEditorSettings,
- } = select( 'core/block-editor' );
+ } = select( 'core/editor' );
const { getPostType } = select( 'core' );
return {
isLocked: isPostLocked(),
isTakeover: isPostLockTakeover(),
user: getPostLockUser(),
postId: getCurrentPostId(),
- // This setting should not live in the block-editor's store.
postLockUtils: getEditorSettings().postLockUtils,
activePostLock: getActivePostLock(),
postType: getPostType( getEditedPostAttribute( 'type' ) ),
diff --git a/packages/editor/src/components/post-title/index.js b/packages/editor/src/components/post-title/index.js
index 761fcff095df4..e992e25bae11a 100644
--- a/packages/editor/src/components/post-title/index.js
+++ b/packages/editor/src/components/post-title/index.js
@@ -150,10 +150,10 @@ class PostTitle extends Component {
const applyWithSelect = withSelect( ( select ) => {
const { getEditedPostAttribute, isCleanNewPost } = select( 'core/editor' );
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getSettings } = select( 'core/block-editor' );
const { getPostType } = select( 'core' );
const postType = getPostType( getEditedPostAttribute( 'type' ) );
- const { titlePlaceholder, focusMode, hasFixedToolbar } = getEditorSettings();
+ const { titlePlaceholder, focusMode, hasFixedToolbar } = getSettings();
return {
isCleanNewPost: isCleanNewPost(),
diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js
index fa85e1b1abadd..92e13d137ce54 100644
--- a/packages/editor/src/components/provider/index.js
+++ b/packages/editor/src/components/provider/index.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { map } from 'lodash';
+import { map, pick } from 'lodash';
import memize from 'memize';
/**
@@ -53,7 +53,22 @@ class EditorProvider extends Component {
getBlockEditorSettings( settings, meta, onMetaChange, reusableBlocks ) {
return {
- ...settings,
+ ...pick( settings, [
+ 'alignWide',
+ 'colors',
+ 'disableCustomColors',
+ 'fontSizes',
+ 'disableCustomFontSizes',
+ 'imageSizes',
+ 'maxWidth',
+ 'blockTypes',
+ 'hasFixedToolbar',
+ 'focusMode',
+ 'styles',
+ 'isRTL',
+ 'bodyPlaceholder',
+ 'titlePlaceholder',
+ ] ),
__experimentalMetaSource: {
value: meta,
onChange: onMetaChange,
@@ -64,6 +79,8 @@ class EditorProvider extends Component {
}
componentDidMount() {
+ this.props.updateEditorSettings( this.props.settings );
+
if ( ! this.props.settings.styles ) {
return;
}
@@ -79,6 +96,12 @@ class EditorProvider extends Component {
} );
}
+ componentDidUpdate( prevProps ) {
+ if ( this.props.settings !== prevProps.settings ) {
+ this.props.updateEditorSettings( this.props.settings );
+ }
+ }
+
render() {
const {
children,
@@ -134,6 +157,7 @@ export default compose( [
updatePostLock,
resetEditorBlocks,
editPost,
+ updateEditorSettings,
} = dispatch( 'core/editor' );
const { createWarningNotice } = dispatch( 'core/notices' );
@@ -142,6 +166,7 @@ export default compose( [
updatePostLock,
createWarningNotice,
resetEditorBlocks,
+ updateEditorSettings,
resetEditorBlocksWithoutUndoLevel( blocks ) {
resetEditorBlocks( blocks, {
__unstableShouldCreateUndoLevel: false,
diff --git a/packages/editor/src/hooks/align.js b/packages/editor/src/hooks/align.js
index b9d2b842cef1d..ca0a019d5cc9e 100644
--- a/packages/editor/src/hooks/align.js
+++ b/packages/editor/src/hooks/align.js
@@ -168,9 +168,9 @@ export const withDataAlign = createHigherOrderComponent(
compose( [
withSelect(
( select ) => {
- const { getEditorSettings } = select( 'core/block-editor' );
+ const { getSettings } = select( 'core/block-editor' );
return {
- hasWideEnabled: !! getEditorSettings().alignWide,
+ hasWideEnabled: !! getSettings().alignWide,
};
}
),
diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js
index f1271ed3d96d1..c0c88d1072fe9 100644
--- a/packages/editor/src/store/actions.js
+++ b/packages/editor/src/store/actions.js
@@ -699,6 +699,20 @@ export function resetEditorBlocks( blocks, options = {} ) {
};
}
+/*
+ * Returns an action object used in signalling that the post editor settings have been updated.
+ *
+ * @param {Object} settings Updated settings
+ *
+ * @return {Object} Action object
+ */
+export function updateEditorSettings( settings ) {
+ return {
+ type: 'UPDATE_EDITOR_SETTINGS',
+ settings,
+ };
+}
+
/**
* Backward compatibility
*/
@@ -737,4 +751,3 @@ export const enterFormattedText = getBlockEditorAction( 'enterFormattedText' );
export const exitFormattedText = getBlockEditorAction( 'exitFormattedText' );
export const insertDefaultBlock = getBlockEditorAction( 'insertDefaultBlock' );
export const updateBlockListSettings = getBlockEditorAction( 'updateBlockListSettings' );
-export const updateEditorSettings = getBlockEditorAction( 'updateEditorSettings' );
diff --git a/packages/editor/src/store/defaults.js b/packages/editor/src/store/defaults.js
index 32fd438302c69..53178b43cb5aa 100644
--- a/packages/editor/src/store/defaults.js
+++ b/packages/editor/src/store/defaults.js
@@ -1,3 +1,8 @@
+/**
+ * WordPress dependencies
+ */
+import { SETTINGS_DEFAULTS } from '@wordpress/block-editor';
+
export const PREFERENCES_DEFAULTS = {
isPublishSidebarEnabled: true,
};
@@ -8,3 +13,22 @@ export const PREFERENCES_DEFAULTS = {
* @type {Object}
*/
export const INITIAL_EDITS_DEFAULTS = {};
+
+/**
+ * The default post editor settings
+ *
+ * richEditingEnabled boolean Whether rich editing is enabled or not
+ * enableCustomFields boolean Whether the WordPress custom fields are enabled or not
+ * autosaveInterval number Autosave Interval
+ * availableTemplates array? The available post templates
+ * disablePostFormats boolean Whether or not the post formats are disabled
+ * allowedMimeTypes array? List of allowed mime types and file extensions
+ * maxUploadFileSize number Maximum upload file size
+ */
+export const EDITOR_SETTINGS_DEFAULTS = {
+ ...SETTINGS_DEFAULTS,
+
+ richEditingEnabled: true,
+ enableCustomFields: false,
+};
+
diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js
index 68a6624163ae0..b6a4759503ce7 100644
--- a/packages/editor/src/store/reducer.js
+++ b/packages/editor/src/store/reducer.js
@@ -24,6 +24,7 @@ import { addQueryArgs } from '@wordpress/url';
import {
PREFERENCES_DEFAULTS,
INITIAL_EDITS_DEFAULTS,
+ EDITOR_SETTINGS_DEFAULTS,
} from './defaults';
import { EDIT_MERGE_PROPERTIES } from './constants';
import withChangeDetection from '../utils/with-change-detection';
@@ -825,6 +826,26 @@ export function isReady( state = false, action ) {
return state;
}
+/**
+ * Reducer returning the post editor setting.
+ *
+ * @param {Object} state Current state.
+ * @param {Object} action Dispatched action.
+ *
+ * @return {Object} Updated state.
+ */
+export function editorSettings( state = EDITOR_SETTINGS_DEFAULTS, action ) {
+ switch ( action.type ) {
+ case 'UPDATE_EDITOR_SETTINGS':
+ return {
+ ...state,
+ ...action.settings,
+ };
+ }
+
+ return state;
+}
+
export default optimist( combineReducers( {
editor,
initialEdits,
@@ -838,4 +859,5 @@ export default optimist( combineReducers( {
previewLink,
postSavingLock,
isReady,
+ editorSettings,
} ) );
diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js
index c4d97cbd741ba..b840175a5cdd9 100644
--- a/packages/editor/src/store/selectors.js
+++ b/packages/editor/src/store/selectors.js
@@ -1084,6 +1084,17 @@ export function __unstableIsEditorReady( state ) {
return state.isReady;
}
+/**
+ * Returns the post editor settings.
+ *
+ * @param {Object} state Editor state.
+ *
+ * @return {Object} The editor settings object.
+ */
+export function getEditorSettings( state ) {
+ return state.editorSettings;
+}
+
/*
* Backward compatibility
*/
@@ -1146,5 +1157,4 @@ export const getTemplateLock = getBlockEditorSelector( 'getTemplateLock' );
export const canInsertBlockType = getBlockEditorSelector( 'canInsertBlockType' );
export const getInserterItems = getBlockEditorSelector( 'getInserterItems' );
export const hasInserterItems = getBlockEditorSelector( 'hasInserterItems' );
-export const getEditorSettings = getBlockEditorSelector( 'getEditorSettings' );
export const getBlockListSettings = getBlockEditorSelector( 'getBlockListSettings' );
diff --git a/packages/editor/src/utils/media-upload/index.js b/packages/editor/src/utils/media-upload/index.js
index ced9e70c6b253..f6572e29c6b0c 100644
--- a/packages/editor/src/utils/media-upload/index.js
+++ b/packages/editor/src/utils/media-upload/index.js
@@ -33,10 +33,7 @@ export default function( {
onError = noop,
onFileChange,
} ) {
- const { getCurrentPostId } = select( 'core/editor' );
- const { getEditorSettings } = select( 'core/block-editor' );
-
- // These settings should not live in the block editor's store.
+ const { getCurrentPostId, getEditorSettings } = select( 'core/editor' );
const wpAllowedMimeTypes = getEditorSettings().allowedMimeTypes;
maxUploadFileSize = maxUploadFileSize || getEditorSettings().maxUploadFileSize;
From c99b7f0c97698b633165eb40ef3100a12de59165 Mon Sep 17 00:00:00 2001
From: Ned Zimmerman
Date: Mon, 4 Mar 2019 05:16:13 -0400
Subject: [PATCH 060/169] Add repository.directory linting rule (fixes #13947)
(#14200)
* Add repository.directory rule to npm-package-json-lint-config (fix #13947)
* Fix lock file (props @aduth)
* Fix rule, update changelogs
* Add PR reference
* Add directory field to packages/docgen
* Apply changes suggested by @ntwb
* Update CHANGELOG.md
---
package-lock.json | 176 +++++++++++-------
package.json | 1 +
packages/docgen/package.json | 3 +-
.../npm-package-json-lint-config/CHANGELOG.md | 8 +-
.../npm-package-json-lint-config/index.js | 1 +
.../npm-package-json-lint-config/package.json | 2 +-
packages/scripts/CHANGELOG.md | 1 +
packages/scripts/package.json | 2 +-
8 files changed, 121 insertions(+), 73 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 2f0d39cd7a38c..44476fc682168 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2908,60 +2908,6 @@
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.10.0",
"enzyme-to-json": "^3.3.5"
- },
- "dependencies": {
- "define-properties": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
- "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
- "dev": true,
- "requires": {
- "object-keys": "^1.0.12"
- }
- },
- "enzyme-adapter-react-16": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.10.0.tgz",
- "integrity": "sha512-0QqwEZcBv1xEEla+a3H7FMci+y4ybLia9cZzsdIrId7qcig4MK0kqqf6iiCILH1lsKS6c6AVqL3wGPhCevv5aQ==",
- "dev": true,
- "requires": {
- "enzyme-adapter-utils": "^1.10.0",
- "object.assign": "^4.1.0",
- "object.values": "^1.1.0",
- "prop-types": "^15.6.2",
- "react-is": "^16.7.0",
- "react-test-renderer": "^16.0.0-0"
- }
- },
- "object.values": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
- "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.12.0",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "prop-types": {
- "version": "15.7.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
- "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
- "dev": true,
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.8.1"
- }
- },
- "react-is": {
- "version": "16.8.3",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz",
- "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==",
- "dev": true
- }
}
},
"@wordpress/jest-puppeteer-axe": {
@@ -3085,7 +3031,7 @@
"eslint": "^5.12.1",
"jest": "^24.1.0",
"jest-puppeteer": "^4.0.0",
- "npm-package-json-lint": "^3.3.1",
+ "npm-package-json-lint": "^3.6.0",
"puppeteer": "1.6.1",
"read-pkg-up": "^1.0.1",
"resolve-bin": "^0.4.0",
@@ -7400,6 +7346,60 @@
"string.prototype.trim": "^1.1.2"
}
},
+ "enzyme-adapter-react-16": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.10.0.tgz",
+ "integrity": "sha512-0QqwEZcBv1xEEla+a3H7FMci+y4ybLia9cZzsdIrId7qcig4MK0kqqf6iiCILH1lsKS6c6AVqL3wGPhCevv5aQ==",
+ "dev": true,
+ "requires": {
+ "enzyme-adapter-utils": "^1.10.0",
+ "object.assign": "^4.1.0",
+ "object.values": "^1.1.0",
+ "prop-types": "^15.6.2",
+ "react-is": "^16.7.0",
+ "react-test-renderer": "^16.0.0-0"
+ },
+ "dependencies": {
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "object.values": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+ "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ },
+ "react-is": {
+ "version": "16.8.3",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz",
+ "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==",
+ "dev": true
+ }
+ }
+ },
"enzyme-adapter-utils": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz",
@@ -14910,29 +14910,30 @@
}
},
"npm-package-json-lint": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/npm-package-json-lint/-/npm-package-json-lint-3.3.1.tgz",
- "integrity": "sha512-Wz5byxFKbitRcQS66wCTqrje/uCLJPH+jsEvIV4H2G//E4iB/X0utVPLZzbs4hukoeKuLFadHbMcILa6Cr027w==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/npm-package-json-lint/-/npm-package-json-lint-3.6.0.tgz",
+ "integrity": "sha512-N1y3r0l0oN7mYnMfRzZvYF8+NvjIx+zkskRn3J7ofipJKGH4RDDKdEGP/mV1Crf5W8uUo3201VhJe04Q+v9erw==",
"dev": true,
"requires": {
- "ajv": "^6.5.2",
- "chalk": "^2.4.1",
- "glob": "^7.1.2",
+ "ajv": "^6.9.2",
+ "chalk": "^2.4.2",
+ "glob": "^7.1.3",
+ "ignore": "^5.0.5",
"is-path-inside": "^2.0.0",
"is-plain-obj": "^1.1.0",
"is-resolvable": "^1.1.0",
"log-symbols": "^2.2.0",
"meow": "^5.0.0",
"plur": "^3.0.1",
- "semver": "^5.5.0",
+ "semver": "^5.6.0",
"strip-json-comments": "^2.0.1",
- "validator": "^10.5.0"
+ "validator": "^10.11.0"
},
"dependencies": {
"ajv": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
- "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+ "version": "6.9.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz",
+ "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
@@ -14941,17 +14942,54 @@
"uri-js": "^4.2.2"
}
},
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "ignore": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz",
+ "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==",
+ "dev": true
+ },
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
}
}
},
@@ -21467,9 +21505,9 @@
}
},
"validator": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/validator/-/validator-10.5.0.tgz",
- "integrity": "sha512-6OOi+eV2mOxCFLq0f2cJDrdB6lrtLXEUxabhNRGjgOLT/l3SSll9J49Cl+LIloUqkWWTPraK/mucEQ3dc2jStQ==",
+ "version": "10.11.0",
+ "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
+ "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==",
"dev": true
},
"vary": {
diff --git a/package.json b/package.json
index b0965325aa435..e0a8cf46f1384 100644
--- a/package.json
+++ b/package.json
@@ -132,6 +132,7 @@
}
],
"require-publishConfig": "error",
+ "require-repository-directory": "error",
"valid-values-author": [
"error",
[
diff --git a/packages/docgen/package.json b/packages/docgen/package.json
index a73ed098f3142..dd94f3b8de2ff 100644
--- a/packages/docgen/package.json
+++ b/packages/docgen/package.json
@@ -12,7 +12,8 @@
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/docgen/README.md",
"repository": {
"type": "git",
- "url": "git+https://github.com/WordPress/gutenberg.git"
+ "url": "git+https://github.com/WordPress/gutenberg.git",
+ "directory": "packages/docgen"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
diff --git a/packages/npm-package-json-lint-config/CHANGELOG.md b/packages/npm-package-json-lint-config/CHANGELOG.md
index d081e98941bc6..1462fbbcd91fc 100644
--- a/packages/npm-package-json-lint-config/CHANGELOG.md
+++ b/packages/npm-package-json-lint-config/CHANGELOG.md
@@ -1,4 +1,10 @@
-## v1.1.3 (2018-09-05)
+## 1.1.4 (Unreleased)
+
+### Internal
+
+- Updated `npm-package-json-lint` dependency [#14200](https://github.com/WordPress/gutenberg/pull/14200)
+
+## 1.1.3 (2018-09-05)
### Bug Fix
diff --git a/packages/npm-package-json-lint-config/index.js b/packages/npm-package-json-lint-config/index.js
index 8b9e5dc2208a1..5af34472e68b1 100644
--- a/packages/npm-package-json-lint-config/index.js
+++ b/packages/npm-package-json-lint-config/index.js
@@ -95,6 +95,7 @@ const defaultConfig = {
'require-private': 'off',
'require-publishConfig': 'off',
'require-repository': 'error',
+ 'require-repository-directory': 'off',
'require-scripts': 'off',
'require-version': 'error',
'scripts-type': 'error',
diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json
index f61270594fd16..d578fc98298f3 100644
--- a/packages/npm-package-json-lint-config/package.json
+++ b/packages/npm-package-json-lint-config/package.json
@@ -23,7 +23,7 @@
},
"main": "index.js",
"peerDependencies": {
- "npm-package-json-lint": ">=3.3.1"
+ "npm-package-json-lint": ">=3.6.0"
},
"publishConfig": {
"access": "public"
diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md
index 342ada51d8374..4e9ea213d597e 100644
--- a/packages/scripts/CHANGELOG.md
+++ b/packages/scripts/CHANGELOG.md
@@ -10,6 +10,7 @@
- Added support for `build` script ([#12837](https://github.com/WordPress/gutenberg/pull/12837))
- Added support for `start` script ([#12837](https://github.com/WordPress/gutenberg/pull/12837))
+- Updated `npm-package-json-lint` dependency [#14200](https://github.com/WordPress/gutenberg/pull/14200)
### Bug Fix
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 6a2f625c182ab..e59064fc82f38 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -43,7 +43,7 @@
"eslint": "^5.12.1",
"jest": "^24.1.0",
"jest-puppeteer": "^4.0.0",
- "npm-package-json-lint": "^3.3.1",
+ "npm-package-json-lint": "^3.6.0",
"puppeteer": "1.6.1",
"read-pkg-up": "^1.0.1",
"resolve-bin": "^0.4.0",
From 91afecf2ba9284cc7f36d2a080dbc9784674b6cc Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Mon, 4 Mar 2019 04:58:52 -0500
Subject: [PATCH 061/169] Plugin: Remove deprecated `_wpLoadGutenbergEditor`,
`gutenberg` theme supports (#14144)
* Plugin: Remove deprecated `gutenberg` theme supports
* Plugin: Remove deprecated `_wpLoadGutenbergEditor`
---
lib/client-assets.php | 40 +++++++++-------------------------------
1 file changed, 9 insertions(+), 31 deletions(-)
diff --git a/lib/client-assets.php b/lib/client-assets.php
index 040557e3cfb11..f721c3855bc0d 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -760,15 +760,9 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
wp_localize_script( 'wp-editor', '_wpMetaBoxUrl', $meta_box_url );
// Initialize the editor.
- $gutenberg_theme_support = get_theme_support( 'gutenberg' );
- $align_wide = get_theme_support( 'align-wide' );
- $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) );
- $font_sizes = current( (array) get_theme_support( 'editor-font-sizes' ) );
-
- if ( ! empty( $gutenberg_theme_support ) ) {
- wp_enqueue_script( 'wp-deprecated' );
- wp_add_inline_script( 'wp-deprecated', 'wp.deprecated( "`gutenberg` theme support", { plugin: "Gutenberg", version: "5.2", alternative: "`align-wide` theme support" } );' );
- }
+ $align_wide = get_theme_support( 'align-wide' );
+ $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) );
+ $font_sizes = current( (array) get_theme_support( 'editor-font-sizes' ) );
/**
* Filters the allowed block types for the editor, defaulting to true (all
@@ -879,7 +873,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
}
$editor_settings = array(
- 'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array.
+ 'alignWide' => $align_wide,
'availableTemplates' => $available_templates,
'allowedBlockTypes' => $allowed_block_types,
'disableCustomColors' => get_theme_support( 'disable-custom-colors' ),
@@ -952,28 +946,12 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
$editor_settings = apply_filters( 'block_editor_settings', $editor_settings, $post );
$init_script = <<
Date: Mon, 4 Mar 2019 11:47:31 +0100
Subject: [PATCH 062/169] Bump plugin version to 5.2.0-rc.1 (#14210)
---
gutenberg.php | 2 +-
package-lock.json | 2 +-
package.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/gutenberg.php b/gutenberg.php
index 993f9e4566834..2ff8f5e26254b 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -3,7 +3,7 @@
* Plugin Name: Gutenberg
* Plugin URI: https://github.com/WordPress/gutenberg
* Description: Printing since 1440. This is the development plugin for the new block editor in core.
- * Version: 5.1.1
+ * Version: 5.2.0-rc.1
* Author: Gutenberg Team
* Text Domain: gutenberg
*
diff --git a/package-lock.json b/package-lock.json
index 44476fc682168..3b660b120761b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "5.1.1",
+ "version": "5.2.0-rc.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index e0a8cf46f1384..fdd2122723c81 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gutenberg",
- "version": "5.1.1",
+ "version": "5.2.0-rc.1",
"private": true,
"description": "A new WordPress editor experience",
"repository": "git+https://github.com/WordPress/gutenberg.git",
From e67de72e8c50d0e9689a97e577ba520320b61edf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?=
Date: Mon, 4 Mar 2019 13:05:02 +0100
Subject: [PATCH 063/169] Packages: Add missing expect-pupeteer dependency to
e2e-tests package (#14212)
---
package-lock.json | 1 +
packages/e2e-tests/package.json | 1 +
2 files changed, 2 insertions(+)
diff --git a/package-lock.json b/package-lock.json
index 3b660b120761b..38cb849991764 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2742,6 +2742,7 @@
"@wordpress/e2e-test-utils": "file:packages/e2e-test-utils",
"@wordpress/jest-console": "file:packages/jest-console",
"@wordpress/scripts": "file:packages/scripts",
+ "expect-puppeteer": "^4.0.0",
"lodash": "^4.17.11"
}
},
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index c7e096b781d76..a10d916e486d3 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -25,6 +25,7 @@
"@wordpress/e2e-test-utils": "file:../e2e-test-utils",
"@wordpress/jest-console": "file:../jest-console",
"@wordpress/scripts": "file:../scripts",
+ "expect-puppeteer": "^4.0.0",
"lodash": "^4.17.11"
},
"peerDependencies": {
From 00454e469955fa699bf7c6e49bd06039ccb5f044 Mon Sep 17 00:00:00 2001
From: Garrett Hyder
Date: Mon, 4 Mar 2019 04:24:23 -0800
Subject: [PATCH 064/169] Lowercase Block Editor to block editor as per the
core spelling Best Practices (#14205)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description
I've updated all references to Block Editor to be lowercase as they aren't proper nouns. This conforms to the core spelling Best Practices;
“block editor” or “block-based editor” | “Block Editor” or “Gutenberg” | When referring to the new editor.
Reference - https://make.wordpress.org/core/handbook/best-practices/spelling/
Also ties back to the original discussion around the capitalization convention here;
https://github.com/WordPress/gutenberg/pull/12856
Previously opened #14203 to handle the Classic Editor changes.
## How has this been tested?
Only tested that it didn't break anything.
## Types of changes
Mostly comment changes, there was one string change and a couple translator comment changes.
## Checklist:
- [x] My code is tested.
- [x] My code follows the WordPress code style.
- [x] My code follows the accessibility standards.
- [x] My code has proper inline documentation.
- [x] I've included developer documentation if appropriate.
---
docs/designers-developers/designers/README.md | 2 +-
.../javascript/extending-the-block-editor.md | 2 +-
.../tutorials/javascript/loading-javascript.md | 4 ++--
.../developers/tutorials/javascript/readme.md | 4 ++--
.../metabox/meta-block-2-register-meta.md | 2 +-
.../tutorials/metabox/meta-block-3-add.md | 2 +-
.../developers/tutorials/metabox/readme.md | 2 +-
.../developers/tutorials/notices/README.md | 18 +++++++++---------
gutenberg.php | 2 +-
packages/block-editor/README.md | 2 +-
packages/block-editor/package.json | 2 +-
11 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/docs/designers-developers/designers/README.md b/docs/designers-developers/designers/README.md
index 362cf794885f2..a630847b37661 100644
--- a/docs/designers-developers/designers/README.md
+++ b/docs/designers-developers/designers/README.md
@@ -1,3 +1,3 @@
# Designer Documentation
-For those designing blocks and other Block Editor integrations, this documentation will provide resources for creating beautiful and intuitive layouts.
+For those designing blocks and other block editor integrations, this documentation will provide resources for creating beautiful and intuitive layouts.
diff --git a/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md b/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md
index 8d175ef3e6d9b..024b1f50e9367 100644
--- a/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md
+++ b/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md
@@ -32,7 +32,7 @@ The last argument in the `wp_enqueue_script()` function is an array of dependenc
See [Packages](/docs/designers-developers/developers/packages.md) for list of available packages and what objects they export.
-After you have updated both JavaScript and PHP files, go to the Block Editor and create a new post.
+After you have updated both JavaScript and PHP files, go to the block editor and create a new post.
Add a quote block, and in the right sidebar under Styles, you will see your new Fancy Quote style listed. Click the Fancy Quote to select and apply that style to your quote block.
diff --git a/docs/designers-developers/developers/tutorials/javascript/loading-javascript.md b/docs/designers-developers/developers/tutorials/javascript/loading-javascript.md
index 813b8fa3e2bdb..c3805f26845cc 100644
--- a/docs/designers-developers/developers/tutorials/javascript/loading-javascript.md
+++ b/docs/designers-developers/developers/tutorials/javascript/loading-javascript.md
@@ -30,7 +30,7 @@ If your code is registered and enqueued correctly, you should see a message in y
![Console Log Message Success](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/assets/js-tutorial-console-log-success.png)
-**Note for Theme Developers:** The above method of enqueing is used for plugins. If you are extending the Block Editor for your theme there is a minor difference, you will use the `get_template_directory_uri()` function instead of `plugins_url()`. So for a theme, the enqueue example is:
+**Note for Theme Developers:** The above method of enqueing is used for plugins. If you are extending the block editor for your theme there is a minor difference, you will use the `get_template_directory_uri()` function instead of `plugins_url()`. So for a theme, the enqueue example is:
```php
function myguten_enqueue() {
@@ -46,4 +46,4 @@ add_action( 'enqueue_block_editor_assets', 'myguten_enqueue' );
At this point, you have a plugin in the directory `wp-content/plugins/myguten-plugin` with two files: the PHP server-side code in `myguten-plugin.php`, and the JavaScript which runs in the browser in `myguten.js`.
-This puts all the initial pieces in place for you to start extending the Block Editor.
+This puts all the initial pieces in place for you to start extending the block editor.
diff --git a/docs/designers-developers/developers/tutorials/javascript/readme.md b/docs/designers-developers/developers/tutorials/javascript/readme.md
index 55628c1cd398e..f425f2726df67 100644
--- a/docs/designers-developers/developers/tutorials/javascript/readme.md
+++ b/docs/designers-developers/developers/tutorials/javascript/readme.md
@@ -1,12 +1,12 @@
# Getting Started with JavaScript
-The purpose of this tutorial is to step through getting started with JavaScript and WordPress, specifically around the new Block Editor. The Gutenberg Handbook documentation contains information on the APIs available for working with the block editor. The goal of this tutorial is get you comfortable on how to use the API reference and snippets of code found within.
+The purpose of this tutorial is to step through getting started with JavaScript and WordPress, specifically around the new block editor. The Gutenberg Handbook documentation contains information on the APIs available for working with the block editor. The goal of this tutorial is get you comfortable on how to use the API reference and snippets of code found within.
### What is JavaScript
JavaScript is a programming language which is loaded and executed in your web browser; compared to PHP which is run by a web server with the results sent to the browser, typically as HTML.
-The Block Editor introduced in WordPress 5.0 is written entirely in JavaScript, with the code run in the browser, and not on the server, this allows for a richer and more dynamic user experience. It also requires to learn how to use JavaScript to extend and enhance the Block Editor.
+The block editor introduced in WordPress 5.0 is written entirely in JavaScript, with the code run in the browser, and not on the server, this allows for a richer and more dynamic user experience. It also requires to learn how to use JavaScript to extend and enhance the block editor.
### Table of Contents
diff --git a/docs/designers-developers/developers/tutorials/metabox/meta-block-2-register-meta.md b/docs/designers-developers/developers/tutorials/metabox/meta-block-2-register-meta.md
index e58889c7599df..795bd722ece29 100644
--- a/docs/designers-developers/developers/tutorials/metabox/meta-block-2-register-meta.md
+++ b/docs/designers-developers/developers/tutorials/metabox/meta-block-2-register-meta.md
@@ -2,7 +2,7 @@
A post meta field is a WordPress object used to store extra data about a post. You need to first register a new meta field prior to use. See Managing [Post Metadata](https://developer.wordpress.org/plugins/metadata/managing-post-metadata/) to learn more about post meta.
-When registering the field, note the `show_in_rest` parameter. This ensures the data will be included in the REST API, which the Block Editor uses to load and save meta data. See the [`register_meta`](https://developer.wordpress.org/reference/functions/register_meta/) function definition for extra information.
+When registering the field, note the `show_in_rest` parameter. This ensures the data will be included in the REST API, which the block editor uses to load and save meta data. See the [`register_meta`](https://developer.wordpress.org/reference/functions/register_meta/) function definition for extra information.
To register the field, create a PHP plugin file called `myguten-meta-block.php` including:
diff --git a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
index e1e6749c17ff7..0a173a539a82b 100644
--- a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
+++ b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md
@@ -6,7 +6,7 @@ For this block, you will use the TextControl component, which is similar to an H
Attributes are the information displayed in blocks. As shown in the block tutorial, the source of attributes come from the text or HTML a user writes in the editor. For your meta block, the attribute will come from the post meta field.
-By specifying the source of the attributes as `meta`, the Block Editor automatically handles the loading and storing of the data; no REST API or data functions are needed.
+By specifying the source of the attributes as `meta`, the block editor automatically handles the loading and storing of the data; no REST API or data functions are needed.
Add this code to your JavaScript file (this tutorial will call the file `myguten.js`):
diff --git a/docs/designers-developers/developers/tutorials/metabox/readme.md b/docs/designers-developers/developers/tutorials/metabox/readme.md
index aa13df05ff580..a1e29fb2beeff 100644
--- a/docs/designers-developers/developers/tutorials/metabox/readme.md
+++ b/docs/designers-developers/developers/tutorials/metabox/readme.md
@@ -4,7 +4,7 @@ Prior to the block editor, custom meta boxes were used to extend the editor. Wit
The new block editor does support most existing meta boxes, see [this backward compatibility article](/docs/designers-developers/developers/backward-compatibility/meta-box.md) for more support details .
-Here are two mini-tutorials for creating similar functionality to meta boxes in the Block Editor.
+Here are two mini-tutorials for creating similar functionality to meta boxes in the block editor.
## Use Blocks to Store Meta
diff --git a/docs/designers-developers/developers/tutorials/notices/README.md b/docs/designers-developers/developers/tutorials/notices/README.md
index 63296f41dfb4d..cde3670e8bf00 100644
--- a/docs/designers-developers/developers/tutorials/notices/README.md
+++ b/docs/designers-developers/developers/tutorials/notices/README.md
@@ -2,7 +2,7 @@
Notices are informational UI displayed near the top of admin pages. WordPress core, themes, and plugins all use notices to indicate the result of an action, or to draw the user's attention to necessary information.
-In the Classic Editor, notices hooked onto the `admin_notices` action can render whatever HTML they'd like. In the Block Editor, notices are restricted to a more formal API.
+In the Classic Editor, notices hooked onto the `admin_notices` action can render whatever HTML they'd like. In the block editor, notices are restricted to a more formal API.
## Notices in the Classic Editor
@@ -33,13 +33,13 @@ function myguten_admin_notice() {
add_action( 'admin_notices', 'myguten_admin_notice' );
```
-Importantly, the `admin_notices` hook allows a developer to render whatever HTML they'd like. One advantage is that the developer has a great amount of flexibility. The key disadvantage is that arbitrary HTML makes future iterations on notices more difficult, if not possible, because the iterations need to accommodate for arbitrary HTML. This is why the Block Editor has a formal API.
+Importantly, the `admin_notices` hook allows a developer to render whatever HTML they'd like. One advantage is that the developer has a great amount of flexibility. The key disadvantage is that arbitrary HTML makes future iterations on notices more difficult, if not possible, because the iterations need to accommodate for arbitrary HTML. This is why the block editor has a formal API.
## Notices in the Block Editor
-In the Block Editor, here's an example of the "Post published" notice:
+In the block editor, here's an example of the "Post published" notice:
-![Post published in the Block Editor](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/tutorials/notices/block-editor-notice.png)
+![Post published in the block editor](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/tutorials/notices/block-editor-notice.png)
Producing an equivalent "Post published" notice would require code like this:
@@ -67,14 +67,14 @@ You'll want to use this _Notices Data API_ when producing a notice from within t
To better understand the specific code example above:
* `wp` is WordPress global window variable.
-* `wp.data` is an object provided by the Block Editor for accessing the Block Editor data store.
-* `wp.data.dispatch('core/notices')` accesses functionality registered to the Block Editor data store by the Notices package.
-* `createNotice()` is a function offered by the Notices package to register a new notice. The Block Editor reads from the notice data store in order to know which notices to display.
+* `wp.data` is an object provided by the block editor for accessing the block editor data store.
+* `wp.data.dispatch('core/notices')` accesses functionality registered to the block editor data store by the Notices package.
+* `createNotice()` is a function offered by the Notices package to register a new notice. The block editor reads from the notice data store in order to know which notices to display.
-Check out the [_Loading JavaScript_](/docs/designers-developers/developers/tutorials/javascript/loading-javascript.md) tutorial for a primer on how to load your custom JavaScript into the Block Editor.
+Check out the [_Loading JavaScript_](/docs/designers-developers/developers/tutorials/javascript/loading-javascript.md) tutorial for a primer on how to load your custom JavaScript into the block editor.
## Learn More
-The Block Editor offers a complete API for generating notices. The official documentation is a great place to review what's possible.
+The block editor offers a complete API for generating notices. The official documentation is a great place to review what's possible.
For a full list of the available actions and selectors, refer to the [Notices Data Handbook](/docs/designers-developers/developers/data/data-core-notices.md) page.
diff --git a/gutenberg.php b/gutenberg.php
index 2ff8f5e26254b..feb70cd894c8b 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -34,7 +34,7 @@ function the_gutenberg_project() {
Classic Editor plugin.', 'gutenberg' ),
+ __( 'The block editor requires JavaScript. Please try the Classic Editor plugin .', 'gutenberg' ),
__( 'https://wordpress.org/plugins/classic-editor/', 'gutenberg' )
);
?>
diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md
index 9e07cf10eb2ce..f58305a4d15d1 100644
--- a/packages/block-editor/README.md
+++ b/packages/block-editor/README.md
@@ -1,6 +1,6 @@
# Block Editor
-Generic Block Editor Module.
+Generic block editor module.
## Installation
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index 6f593a1063edf..232ed8a681fcd 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -1,7 +1,7 @@
{
"name": "@wordpress/block-editor",
"version": "1.0.0-alpha.0",
- "description": "Generic Block Editor.",
+ "description": "Generic block editor.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"keywords": [
From ff25bd4ade282053b1c2e71fd8fbd80fcbdb05b4 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Mon, 4 Mar 2019 16:39:23 -0500
Subject: [PATCH 065/169] Babel Preset Default: Avoid disabling regenerator
option (#14130)
* Babel Preset Default: Remove redundant (defaulted) corejs option
* Scripts: Assign Babel runtime regenerator as externals in Webpack config
* Babel Preset Default: Avoid disabling regenerator option
---
packages/babel-preset-default/CHANGELOG.md | 8 ++++++--
packages/babel-preset-default/index.js | 2 --
.../test/__snapshots__/index.js.snap | 9 +++++----
packages/scripts/config/webpack.config.js | 8 ++++++++
4 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md
index 3fe7a9f29b100..2f2ce6a633962 100644
--- a/packages/babel-preset-default/CHANGELOG.md
+++ b/packages/babel-preset-default/CHANGELOG.md
@@ -1,13 +1,17 @@
## 4.0.0 (Unreleased)
-## Breaking Change
+### Breaking Change
- Removed `babel-core` dependency acting as Babel 7 bridge ([#13922](https://github.com/WordPress/gutenberg/pull/13922). Ensure all references to `babel-core` are replaced with `@babel/core` .
- Preset updated to include `@wordpress/babel-plugin-import-jsx-pragma` plugin integration ([#13540](https://github.com/WordPress/gutenberg/pull/13540)).
+### Bug Fix
+
+- The runtime transform no longer disables [the `regenerator` option](https://babeljs.io/docs/en/babel-plugin-transform-runtime#regenerator). This should resolve issues where a file generated using the preset would assume the presence of a `regeneratorRuntime` object in the global scope. While this is not considered a breaking change, you may be mindful to consider that with transformed output now explicitly importing the runtime regenerator, bundle sizes may increase if you do not otherwise mitigate the additional import by either (a) overriding the option in your own Babel configuration extensions or (b) redefining the resolved value of `@babel/runtime/regenerator` using a feature like [Webpack's `externals` option](https://webpack.js.org/configuration/externals/).
+
## 3.0.0 (2018-09-30)
-## Breaking Change
+### Breaking Change
- The configured `@babel/preset-env` preset will no longer pass `useBuiltIns: 'usage'` as an option. It is therefore expected that a polyfill serve in its place, if necessary.
diff --git a/packages/babel-preset-default/index.js b/packages/babel-preset-default/index.js
index 44a0392cd82cb..8a893ee9c5ea7 100644
--- a/packages/babel-preset-default/index.js
+++ b/packages/babel-preset-default/index.js
@@ -28,9 +28,7 @@ module.exports = function( api ) {
} ],
'@babel/plugin-proposal-async-generator-functions',
! isTestEnv && [ '@babel/plugin-transform-runtime', {
- corejs: false, // We polyfill so we don't need core-js.
helpers: true,
- regenerator: false, // We polyfill so we don't need regenerator.
useESModules: false,
} ],
].filter( Boolean ),
diff --git a/packages/babel-preset-default/test/__snapshots__/index.js.snap b/packages/babel-preset-default/test/__snapshots__/index.js.snap
index 5cf000511103d..ed8a87a05dbfe 100644
--- a/packages/babel-preset-default/test/__snapshots__/index.js.snap
+++ b/packages/babel-preset-default/test/__snapshots__/index.js.snap
@@ -2,6 +2,7 @@
exports[`Babel preset default transpilation works properly 1`] = `
"import _asyncToGenerator from \\"@babel/runtime/helpers/asyncToGenerator\\";
+import _regeneratorRuntime from \\"@babel/runtime/regenerator\\";
import _awaitAsyncGenerator from \\"@babel/runtime/helpers/awaitAsyncGenerator\\";
import _wrapAsyncGenerator from \\"@babel/runtime/helpers/wrapAsyncGenerator\\";
describe('Babel preset default', function () {
@@ -12,8 +13,8 @@ describe('Babel preset default', function () {
function _foo() {
_foo = _wrapAsyncGenerator(
/*#__PURE__*/
- regeneratorRuntime.mark(function _callee() {
- return regeneratorRuntime.wrap(function _callee$(_context) {
+ _regeneratorRuntime.mark(function _callee() {
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
@@ -38,9 +39,9 @@ describe('Babel preset default', function () {
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
- regeneratorRuntime.mark(function _callee2() {
+ _regeneratorRuntime.mark(function _callee2() {
var generator;
- return regeneratorRuntime.wrap(function _callee2$(_context2) {
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
diff --git a/packages/scripts/config/webpack.config.js b/packages/scripts/config/webpack.config.js
index 44d0ff61d9a65..70fed30229ba4 100644
--- a/packages/scripts/config/webpack.config.js
+++ b/packages/scripts/config/webpack.config.js
@@ -51,6 +51,14 @@ const externals = [
jquery: 'jQuery',
lodash: 'lodash',
'lodash-es': 'lodash',
+
+ // Distributed NPM packages may depend on Babel's runtime regenerator.
+ // In a WordPress context, the regenerator is assigned to the global
+ // scope via the `wp-polyfill` script. It is reassigned here as an
+ // externals to reduce the size of generated bundles.
+ //
+ // See: https://github.com/WordPress/gutenberg/issues/13890
+ '@babel/runtime/regenerator': 'regeneratorRuntime',
},
wordpressExternals,
];
From 7c952cf24823b36251512dc433d1041d2c5c50ed Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Tue, 5 Mar 2019 02:43:06 -0500
Subject: [PATCH 066/169] Testing: Await E2E promise interactions (#14219)
* Testing: Await E2E promise interactions
* Testing: Disable animations on every admin screen
* Testing: Refactor reusable block management to compare before/after entry count
---
.../e2e-tests/plugins/disable-animations.php | 2 +-
.../specs/block-hierarchy-navigation.test.js | 8 ++++----
.../e2e-tests/specs/change-detection.test.js | 4 ++--
packages/e2e-tests/specs/demo.test.js | 2 +-
.../specs/manage-reusable-blocks.test.js | 18 ++++++++++++++++--
packages/e2e-tests/specs/taxonomies.test.js | 2 +-
6 files changed, 25 insertions(+), 11 deletions(-)
diff --git a/packages/e2e-tests/plugins/disable-animations.php b/packages/e2e-tests/plugins/disable-animations.php
index f13c2c9003a2d..4e69a6ba88f89 100644
--- a/packages/e2e-tests/plugins/disable-animations.php
+++ b/packages/e2e-tests/plugins/disable-animations.php
@@ -15,4 +15,4 @@ function enqueue_disable_animations_stylesheet() {
wp_add_inline_style( 'wp-components', $custom_css );
}
-add_action( 'enqueue_block_editor_assets', 'enqueue_disable_animations_stylesheet' );
+add_action( 'admin_enqueue_scripts', 'enqueue_disable_animations_stylesheet' );
diff --git a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js b/packages/e2e-tests/specs/block-hierarchy-navigation.test.js
index 4745768f812c7..28b5c88b9d2c2 100644
--- a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js
+++ b/packages/e2e-tests/specs/block-hierarchy-navigation.test.js
@@ -31,10 +31,10 @@ describe( 'Navigating the block hierarchy', () => {
// Tweak the columns count.
await page.focus( '.edit-post-settings-sidebar__panel-block .components-range-control__number[aria-label="Columns"]' );
- page.keyboard.down( 'Shift' );
- page.keyboard.press( 'ArrowLeft' );
- page.keyboard.up( 'Shift' );
- page.keyboard.type( '3' );
+ await page.keyboard.down( 'Shift' );
+ await page.keyboard.press( 'ArrowLeft' );
+ await page.keyboard.up( 'Shift' );
+ await page.keyboard.type( '3' );
// Navigate to the last column block.
await page.click( '[aria-label="Block Navigation"]' );
diff --git a/packages/e2e-tests/specs/change-detection.test.js b/packages/e2e-tests/specs/change-detection.test.js
index b8f2377ce2c91..0e529a95ca512 100644
--- a/packages/e2e-tests/specs/change-detection.test.js
+++ b/packages/e2e-tests/specs/change-detection.test.js
@@ -18,9 +18,9 @@ describe( 'Change detection', () => {
await createNewPost();
} );
- afterEach( () => {
+ afterEach( async () => {
if ( handleInterceptedRequest ) {
- releaseSaveIntercept();
+ await releaseSaveIntercept();
}
} );
diff --git a/packages/e2e-tests/specs/demo.test.js b/packages/e2e-tests/specs/demo.test.js
index 80d3fbfeb2939..73fc77966cf6d 100644
--- a/packages/e2e-tests/specs/demo.test.js
+++ b/packages/e2e-tests/specs/demo.test.js
@@ -22,7 +22,7 @@ const stripIframeFromEmbed = ( embedObject ) => {
describe( 'new editor state', () => {
beforeAll( async () => {
- setUpResponseMocking( [
+ await setUpResponseMocking( [
{
match: createURLMatcher( 'oembed%2F1.0%2Fproxy' ),
onRequestMatch: mockOrTransform( couldNotBePreviewed, MOCK_VIMEO_RESPONSE, stripIframeFromEmbed ),
diff --git a/packages/e2e-tests/specs/manage-reusable-blocks.test.js b/packages/e2e-tests/specs/manage-reusable-blocks.test.js
index 05af841189a83..e846de123004d 100644
--- a/packages/e2e-tests/specs/manage-reusable-blocks.test.js
+++ b/packages/e2e-tests/specs/manage-reusable-blocks.test.js
@@ -9,11 +9,23 @@ import path from 'path';
import { visitAdminPage } from '@wordpress/e2e-test-utils';
describe( 'Managing reusable blocks', () => {
+ /**
+ * Returns a Promise which resolves to the number of post list entries on
+ * the current page.
+ *
+ * @return {Promise} Promise resolving to number of post list entries.
+ */
+ async function getNumberOfEntries() {
+ return page.evaluate( () => document.querySelectorAll( '.entry' ).length );
+ }
+
beforeAll( async () => {
await visitAdminPage( 'edit.php', 'post_type=wp_block' );
} );
it( 'Should import reusable blocks', async () => {
+ const originalEntries = await getNumberOfEntries();
+
// Import Reusable block
await page.waitForSelector( '.list-reusable-blocks__container' );
const importButton = await page.$( '.list-reusable-blocks__container button' );
@@ -36,7 +48,9 @@ describe( 'Managing reusable blocks', () => {
// Refresh the page
await visitAdminPage( 'edit.php', 'post_type=wp_block' );
- // The reusable block has been imported
- page.waitForXPath( 'div[@class="post_title"][contains(text(), "Greeting")]' );
+ const expectedEntries = originalEntries + 1;
+ const actualEntries = await getNumberOfEntries();
+
+ expect( actualEntries ).toBe( expectedEntries );
} );
} );
diff --git a/packages/e2e-tests/specs/taxonomies.test.js b/packages/e2e-tests/specs/taxonomies.test.js
index 087da816f4d6a..ee3759fe296f0 100644
--- a/packages/e2e-tests/specs/taxonomies.test.js
+++ b/packages/e2e-tests/specs/taxonomies.test.js
@@ -153,7 +153,7 @@ describe( 'Taxonomies', () => {
await page.reload();
// Wait for the tags to load.
- page.waitForSelector( '.components-form-token-field__token' );
+ await page.waitForSelector( '.components-form-token-field__token' );
tags = await getCurrentTags();
From 087c4aa22fe6c537895d25572b449c6971731eb4 Mon Sep 17 00:00:00 2001
From: Garrett Hyder
Date: Mon, 4 Mar 2019 23:55:12 -0800
Subject: [PATCH 067/169] Lowercase classic editor when not in reference to the
plugin. (#14203)
* Lowercase classic editor when not in reference to the plugin.
* Reverted heading to use capitalized as the heading choice throughout uses full capitalization style on headings so went back to make consistent as @torres126 recommended. Also updated the snap file to resolve the Travis CI error.
* Missed saving README.md where the revert on headding capitalization was in my editor.
---
.../developers/backward-compatibility/meta-box.md | 2 +-
.../developers/filters/editor-filters.md | 2 +-
.../developers/tutorials/notices/README.md | 6 +++---
docs/designers-developers/faq.md | 2 +-
.../__snapshots__/compatibility-classic-editor.test.js.snap | 2 +-
.../e2e-tests/specs/compatibility-classic-editor.test.js | 2 +-
6 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/docs/designers-developers/developers/backward-compatibility/meta-box.md b/docs/designers-developers/developers/backward-compatibility/meta-box.md
index affb67fa65a10..e7350dadba7fb 100644
--- a/docs/designers-developers/developers/backward-compatibility/meta-box.md
+++ b/docs/designers-developers/developers/backward-compatibility/meta-box.md
@@ -30,7 +30,7 @@ add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback',
);
```
-When Gutenberg is used, this meta box will no longer be displayed in the meta box area, as it now only exists for backward compatibility purposes. It will continue to display correctly in the Classic editor.
+When Gutenberg is used, this meta box will no longer be displayed in the meta box area, as it now only exists for backward compatibility purposes. It will continue to display correctly in the classic editor.
### Meta Box Data Collection
diff --git a/docs/designers-developers/developers/filters/editor-filters.md b/docs/designers-developers/developers/filters/editor-filters.md
index 2ddb34f39f957..9421d7cd68801 100644
--- a/docs/designers-developers/developers/filters/editor-filters.md
+++ b/docs/designers-developers/developers/filters/editor-filters.md
@@ -4,7 +4,7 @@ To modify the behavior of the editor experience, the following Filters are expos
### `editor.PostFeaturedImage.imageSize`
-Used to modify the image size displayed in the Post Featured Image component. It defaults to `'post-thumbnail'`, and will fail back to the `full` image size when the specified image size doesn't exist in the media object. It's modeled after the `admin_post_thumbnail_size` filter in the Classic Editor.
+Used to modify the image size displayed in the Post Featured Image component. It defaults to `'post-thumbnail'`, and will fail back to the `full` image size when the specified image size doesn't exist in the media object. It's modeled after the `admin_post_thumbnail_size` filter in the classic editor.
_Example:_
diff --git a/docs/designers-developers/developers/tutorials/notices/README.md b/docs/designers-developers/developers/tutorials/notices/README.md
index cde3670e8bf00..56950343a13ce 100644
--- a/docs/designers-developers/developers/tutorials/notices/README.md
+++ b/docs/designers-developers/developers/tutorials/notices/README.md
@@ -2,13 +2,13 @@
Notices are informational UI displayed near the top of admin pages. WordPress core, themes, and plugins all use notices to indicate the result of an action, or to draw the user's attention to necessary information.
-In the Classic Editor, notices hooked onto the `admin_notices` action can render whatever HTML they'd like. In the block editor, notices are restricted to a more formal API.
+In the classic editor, notices hooked onto the `admin_notices` action can render whatever HTML they'd like. In the block editor, notices are restricted to a more formal API.
## Notices in the Classic Editor
-In the Classic Editor, here's an example of the "Post draft updated" notice:
+In the classic editor, here's an example of the "Post draft updated" notice:
-![Post draft updated in the Classic Editor](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/tutorials/notices/classic-editor-notice.png)
+![Post draft updated in the classic editor](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/tutorials/notices/classic-editor-notice.png)
Producing an equivalent "Post draft updated" notice would require code like this:
diff --git a/docs/designers-developers/faq.md b/docs/designers-developers/faq.md
index eb4b21b2a1a6c..4917ac8f82e3c 100644
--- a/docs/designers-developers/faq.md
+++ b/docs/designers-developers/faq.md
@@ -322,7 +322,7 @@ We realize it's a big change. We also think there will be many new opportunities
There is a “Classic” block, which is virtually the same as the current editor, except in block form.
-There is also the [Classic Editor Plugin](https://wordpress.org/plugins/classic-editor/) which restores the previous editor, see the plugin for more information. The WordPress Core team has committed to supporting the Classic Editor Plugin [until December 2021](https://make.wordpress.org/core/2018/11/07/classic-editor-plugin-support-window/).
+There is also the [Classic Editor plugin](https://wordpress.org/plugins/classic-editor/) which restores the previous editor, see the plugin for more information. The WordPress Core team has committed to supporting the Classic Editor plugin [until December 2021](https://make.wordpress.org/core/2018/11/07/classic-editor-plugin-support-window/).
## How will custom TinyMCE buttons work in Gutenberg?
diff --git a/packages/e2e-tests/specs/__snapshots__/compatibility-classic-editor.test.js.snap b/packages/e2e-tests/specs/__snapshots__/compatibility-classic-editor.test.js.snap
index e310008a220d3..3115d36a3b64c 100644
--- a/packages/e2e-tests/specs/__snapshots__/compatibility-classic-editor.test.js.snap
+++ b/packages/e2e-tests/specs/__snapshots__/compatibility-classic-editor.test.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Compatibility with Classic Editor Should not apply autop when rendering blocks 1`] = `
+exports[`Compatibility with classic editor Should not apply autop when rendering blocks 1`] = `
"
Random Link
"
diff --git a/packages/e2e-tests/specs/compatibility-classic-editor.test.js b/packages/e2e-tests/specs/compatibility-classic-editor.test.js
index 6134361af73fe..9b311275b7296 100644
--- a/packages/e2e-tests/specs/compatibility-classic-editor.test.js
+++ b/packages/e2e-tests/specs/compatibility-classic-editor.test.js
@@ -3,7 +3,7 @@
*/
import { createNewPost, insertBlock, publishPost } from '@wordpress/e2e-test-utils';
-describe( 'Compatibility with Classic Editor', () => {
+describe( 'Compatibility with classic editor', () => {
beforeEach( async () => {
await createNewPost();
} );
From 44c58b8d72b152c9df94c6e976ff3dfe865e0ea8 Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Tue, 5 Mar 2019 09:00:26 +0000
Subject: [PATCH 068/169] Fix: Calendar block: Always show current month for
non post types on the editor (#13873)
---
packages/block-library/src/calendar/edit.js | 11 +++++++++-
packages/block-library/src/calendar/index.php | 22 +++++++++++--------
2 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/packages/block-library/src/calendar/edit.js b/packages/block-library/src/calendar/edit.js
index 67d89d99bd457..f884490ec38c8 100644
--- a/packages/block-library/src/calendar/edit.js
+++ b/packages/block-library/src/calendar/edit.js
@@ -61,7 +61,16 @@ class CalendarEdit extends Component {
}
export default withSelect( ( select ) => {
+ const {
+ getEditedPostAttribute,
+ } = select( 'core/editor' );
+ const postType = getEditedPostAttribute( 'type' );
+ // Dates are used to overwrite year and month used on the calendar.
+ // This overwrite should only happen for 'post' post types.
+ // For other post types the calendar always displays the current month.
return {
- date: select( 'core/editor' ).getEditedPostAttribute( 'date' ),
+ date: postType === 'post' ?
+ getEditedPostAttribute( 'date' ) :
+ undefined,
};
} )( CalendarEdit );
diff --git a/packages/block-library/src/calendar/index.php b/packages/block-library/src/calendar/index.php
index 9177997692f40..8e26626f5f861 100644
--- a/packages/block-library/src/calendar/index.php
+++ b/packages/block-library/src/calendar/index.php
@@ -13,18 +13,22 @@
* @return string Returns the block content.
*/
function render_block_core_calendar( $attributes ) {
- global $monthnum, $year, $post;
+ global $monthnum, $year;
+
$previous_monthnum = $monthnum;
$previous_year = $year;
- if ( isset( $attributes['month'] ) ) {
- // phpcs:ignore WordPress.WP.GlobalVariablesOverride.OverrideProhibited
- $monthnum = $attributes['month'];
- }
-
- if ( isset( $attributes['year'] ) ) {
- // phpcs:ignore WordPress.WP.GlobalVariablesOverride.OverrideProhibited
- $year = $attributes['year'];
+ if ( isset( $attributes['month'] ) && isset( $attributes['year'] ) ) {
+ $permalink_structure = get_option( 'permalink_structure' );
+ if (
+ strpos( $permalink_structure, '%monthnum%' ) !== false &&
+ strpos( $permalink_structure, '%year%' ) !== false
+ ) {
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.OverrideProhibited
+ $monthnum = $attributes['month'];
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.OverrideProhibited
+ $year = $attributes['year'];
+ }
}
$custom_class_name = empty( $attributes['className'] ) ? '' : ' ' . $attributes['className'];
From 357175ff246352d0aa74a6f1140ed8fd0fb8cb5a Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Tue, 5 Mar 2019 10:10:04 +0100
Subject: [PATCH 069/169] Fix allowed_block_types regression (#14229)
---
packages/block-editor/src/store/defaults.js | 2 +-
packages/e2e-tests/plugins/allowed-blocks.php | 24 +++++++++++++
.../specs/plugins/allowed-blocks.test.js | 36 +++++++++++++++++++
.../editor/src/components/provider/index.js | 2 +-
4 files changed, 62 insertions(+), 2 deletions(-)
create mode 100644 packages/e2e-tests/plugins/allowed-blocks.php
create mode 100644 packages/e2e-tests/specs/plugins/allowed-blocks.test.js
diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js
index 3e61e459c53f6..728b500a51d59 100644
--- a/packages/block-editor/src/store/defaults.js
+++ b/packages/block-editor/src/store/defaults.js
@@ -17,7 +17,7 @@ export const PREFERENCES_DEFAULTS = {
* disableCustomFontSizes boolean Whether or not the custom font sizes are disabled
* imageSizes Array Available image sizes
* maxWidth number Max width to constraint resizing
- * blockTypes boolean|Array Allowed block types
+ * allowedBlockTypes boolean|Array Allowed block types
* hasFixedToolbar boolean Whether or not the editor toolbar is fixed
* focusMode boolean Whether the focus mode is enabled or not
* styles Array Editor Styles
diff --git a/packages/e2e-tests/plugins/allowed-blocks.php b/packages/e2e-tests/plugins/allowed-blocks.php
new file mode 100644
index 0000000000000..c3d30cb20c9bd
--- /dev/null
+++ b/packages/e2e-tests/plugins/allowed-blocks.php
@@ -0,0 +1,24 @@
+post_type ) {
+ return $allowed_block_types;
+ }
+ return array( 'core/paragraph', 'core/image' );
+}
+
+add_filter( 'allowed_block_types', 'my_plugin_allowed_block_types', 10, 2 );
diff --git a/packages/e2e-tests/specs/plugins/allowed-blocks.test.js b/packages/e2e-tests/specs/plugins/allowed-blocks.test.js
new file mode 100644
index 0000000000000..9e86e4ad01990
--- /dev/null
+++ b/packages/e2e-tests/specs/plugins/allowed-blocks.test.js
@@ -0,0 +1,36 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ activatePlugin,
+ createNewPost,
+ deactivatePlugin,
+ searchForBlock,
+} from '@wordpress/e2e-test-utils';
+
+describe( 'Allowed Blocks Filter', () => {
+ beforeAll( async () => {
+ await activatePlugin( 'gutenberg-test-allowed-blocks' );
+ } );
+
+ beforeEach( async () => {
+ await createNewPost();
+ } );
+
+ afterAll( async () => {
+ await deactivatePlugin( 'gutenberg-test-allowed-blocks' );
+ } );
+
+ it( 'should restrict the allowed blocks in the inserter', async () => {
+ // The paragraph block is available.
+ await searchForBlock( 'Paragraph' );
+ const paragraphBlock = await page.$( `button[aria-label="Paragraph"]` );
+ expect( paragraphBlock ).not.toBeNull();
+ await paragraphBlock.click();
+
+ // The gallery block is not available.
+ await searchForBlock( 'Gallery' );
+ const galleryBlock = await page.$( `button[aria-label="Gallery"]` );
+ expect( galleryBlock ).toBeNull();
+ } );
+} );
diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js
index 92e13d137ce54..8817eb5d4be3b 100644
--- a/packages/editor/src/components/provider/index.js
+++ b/packages/editor/src/components/provider/index.js
@@ -61,7 +61,7 @@ class EditorProvider extends Component {
'disableCustomFontSizes',
'imageSizes',
'maxWidth',
- 'blockTypes',
+ 'allowedBlockTypes',
'hasFixedToolbar',
'focusMode',
'styles',
From ff5360861a98cb8f526b26492568bf3764120930 Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Tue, 5 Mar 2019 09:29:21 +0000
Subject: [PATCH 070/169] Fix: Latest posts: Title is clickable across the full
width of the row (#14109)
## Description
The title in latest posts block was clickable across the full width of the row, making it easy to click on the title by mistake.
This problem only affects the editor on the front end things worked as expected.
## How has this been tested?
I added the latest posts block.
I verified that the title is only clickable if the mouse is above the title and not above any part of its row.
## Screenshots
Before:
![feb-25-2019 21-49-15](https://user-images.githubusercontent.com/11271197/53371643-b3e3f380-3948-11e9-95f5-a8f235fabab2.gif)
After:
![feb-25-2019 21-53-30](https://user-images.githubusercontent.com/11271197/53371657-c2320f80-3948-11e9-8dfd-ab7ae4640955.gif)
---
packages/block-library/src/latest-posts/editor.scss | 3 +++
1 file changed, 3 insertions(+)
diff --git a/packages/block-library/src/latest-posts/editor.scss b/packages/block-library/src/latest-posts/editor.scss
index a29b74fd4cc3a..3442630f42486 100644
--- a/packages/block-library/src/latest-posts/editor.scss
+++ b/packages/block-library/src/latest-posts/editor.scss
@@ -4,3 +4,6 @@
padding-left: 0;
}
}
+.wp-block-latest-posts li a > div {
+ display: inline;
+}
From 99f294cd8683283af6550f07bca41c94dacf34d4 Mon Sep 17 00:00:00 2001
From: Andrew Duthie
Date: Tue, 5 Mar 2019 04:44:46 -0500
Subject: [PATCH 071/169] Blocks: Regenerate RSS, Search block fixtures
(#14122)
---
packages/e2e-tests/fixtures/blocks/core__rss.html | 2 +-
packages/e2e-tests/fixtures/blocks/core__rss.json | 11 ++++++++++-
.../e2e-tests/fixtures/blocks/core__rss.parsed.json | 4 ++--
.../fixtures/blocks/core__rss.serialized.html | 2 +-
packages/e2e-tests/fixtures/blocks/core__search.json | 6 +++++-
.../fixtures/blocks/core__search__custom-text.json | 6 +++++-
.../blocks/core__search__custom-text.parsed.json | 4 ++--
.../blocks/core__search__custom-text.serialized.html | 2 +-
test/integration/full-content/server-registered.json | 2 +-
9 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/packages/e2e-tests/fixtures/blocks/core__rss.html b/packages/e2e-tests/fixtures/blocks/core__rss.html
index ce652528301c6..69b41ce041862 100644
--- a/packages/e2e-tests/fixtures/blocks/core__rss.html
+++ b/packages/e2e-tests/fixtures/blocks/core__rss.html
@@ -1 +1 @@
-
+
diff --git a/packages/e2e-tests/fixtures/blocks/core__rss.json b/packages/e2e-tests/fixtures/blocks/core__rss.json
index 89de06cd2d78d..09f43cf2e1e25 100644
--- a/packages/e2e-tests/fixtures/blocks/core__rss.json
+++ b/packages/e2e-tests/fixtures/blocks/core__rss.json
@@ -3,7 +3,16 @@
"clientId": "_clientId_0",
"name": "core/rss",
"isValid": true,
- "attributes": {},
+ "attributes": {
+ "columns": 2,
+ "blockLayout": "grid",
+ "feedURL": "https://wordpress.org/news/",
+ "itemsToShow": 4,
+ "displayExcerpt": true,
+ "displayAuthor": true,
+ "displayDate": true,
+ "excerptLength": 20
+ },
"innerBlocks": [],
"originalContent": ""
}
diff --git a/packages/e2e-tests/fixtures/blocks/core__rss.parsed.json b/packages/e2e-tests/fixtures/blocks/core__rss.parsed.json
index bdc596b09a5a1..e55306214e219 100644
--- a/packages/e2e-tests/fixtures/blocks/core__rss.parsed.json
+++ b/packages/e2e-tests/fixtures/blocks/core__rss.parsed.json
@@ -2,9 +2,9 @@
{
"blockName": "core/rss",
"attrs": {
- "postLayout": "grid",
+ "blockLayout": "grid",
"feedURL": "https://wordpress.org/news/",
- "postsToShow": 4,
+ "itemsToShow": 4,
"displayExcerpt": true,
"displayAuthor": true,
"displayDate": true,
diff --git a/packages/e2e-tests/fixtures/blocks/core__rss.serialized.html b/packages/e2e-tests/fixtures/blocks/core__rss.serialized.html
index c6d9be22646e7..69b41ce041862 100644
--- a/packages/e2e-tests/fixtures/blocks/core__rss.serialized.html
+++ b/packages/e2e-tests/fixtures/blocks/core__rss.serialized.html
@@ -1 +1 @@
-
+
diff --git a/packages/e2e-tests/fixtures/blocks/core__search.json b/packages/e2e-tests/fixtures/blocks/core__search.json
index e1397fdc9a37a..dbf8ea090062e 100644
--- a/packages/e2e-tests/fixtures/blocks/core__search.json
+++ b/packages/e2e-tests/fixtures/blocks/core__search.json
@@ -3,7 +3,11 @@
"clientId": "_clientId_0",
"name": "core/search",
"isValid": true,
- "attributes": {},
+ "attributes": {
+ "label": "Search",
+ "placeholder": "",
+ "buttonText": "Search"
+ },
"innerBlocks": [],
"originalContent": ""
}
diff --git a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json
index e1397fdc9a37a..fb91161919c83 100644
--- a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json
+++ b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json
@@ -3,7 +3,11 @@
"clientId": "_clientId_0",
"name": "core/search",
"isValid": true,
- "attributes": {},
+ "attributes": {
+ "label": "Custom label",
+ "placeholder": "Custom placeholder",
+ "buttonText": "Custom button text"
+ },
"innerBlocks": [],
"originalContent": ""
}
diff --git a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.parsed.json b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.parsed.json
index fd128b35f93c8..6b67b6d21a472 100644
--- a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.parsed.json
+++ b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.parsed.json
@@ -2,9 +2,9 @@
{
"blockName": "core/search",
"attrs": {
- "buttonText": "Custom button text",
"label": "Custom label",
- "placeholder": "Custom placeholder"
+ "placeholder": "Custom placeholder",
+ "buttonText": "Custom button text"
},
"innerBlocks": [],
"innerHTML": "",
diff --git a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.serialized.html b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.serialized.html
index f62e8a853c2ea..bb6e6a56c9a33 100644
--- a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.serialized.html
+++ b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.serialized.html
@@ -1 +1 @@
-
+
diff --git a/test/integration/full-content/server-registered.json b/test/integration/full-content/server-registered.json
index 080865f39bb10..eeae11cb99e9c 100644
--- a/test/integration/full-content/server-registered.json
+++ b/test/integration/full-content/server-registered.json
@@ -1 +1 @@
-{"core\/block":{"attributes":{"ref":{"type":"number"}}},"core\/latest-comments":{"attributes":{"className":{"type":"string"},"commentsToShow":{"type":"number","default":5,"minimum":1,"maximum":100},"displayAvatar":{"type":"boolean","default":true},"displayDate":{"type":"boolean","default":true},"displayExcerpt":{"type":"boolean","default":true},"align":{"type":"string","enum":["center","left","right","wide","full",""]}}},"core\/archives":{"attributes":{"align":{"type":"string"},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/latest-posts":{"attributes":{"categories":{"type":"string"},"className":{"type":"string"},"postsToShow":{"type":"number","default":5},"displayPostDate":{"type":"boolean","default":false},"postLayout":{"type":"string","default":"list"},"columns":{"type":"number","default":3},"align":{"type":"string"},"order":{"type":"string","default":"desc"},"orderBy":{"type":"string","default":"date"}}},"core\/tag-cloud":{"attributes":{"taxonomy":{"type":"string","default":"tags"},"className":{"type":"string"},"showTagCounts":{"type":"boolean","default":false},"align":{"type":"string"}}}}
\ No newline at end of file
+{"core\/block":{"attributes":{"ref":{"type":"number"}}},"core\/latest-comments":{"attributes":{"className":{"type":"string"},"commentsToShow":{"type":"number","default":5,"minimum":1,"maximum":100},"displayAvatar":{"type":"boolean","default":true},"displayDate":{"type":"boolean","default":true},"displayExcerpt":{"type":"boolean","default":true},"align":{"type":"string","enum":["center","left","right","wide","full",""]}}},"core\/archives":{"attributes":{"align":{"type":"string"},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/calendar":{"attributes":{"align":{"type":"string"},"className":{"type":"string"},"month":{"type":"integer"},"year":{"type":"integer"}}},"core\/latest-posts":{"attributes":{"categories":{"type":"string"},"className":{"type":"string"},"postsToShow":{"type":"number","default":5},"displayPostDate":{"type":"boolean","default":false},"postLayout":{"type":"string","default":"list"},"columns":{"type":"number","default":3},"align":{"type":"string"},"order":{"type":"string","default":"desc"},"orderBy":{"type":"string","default":"date"}}},"core\/rss":{"attributes":{"columns":{"type":"number","default":2},"blockLayout":{"type":"string","default":"list"},"feedURL":{"type":"string","default":""},"itemsToShow":{"type":"number","default":5},"displayExcerpt":{"type":"boolean","default":false},"displayAuthor":{"type":"boolean","default":false},"displayDate":{"type":"boolean","default":false},"excerptLength":{"type":"number","default":55}}},"core\/search":{"attributes":{"label":{"type":"string","default":"Search"},"placeholder":{"type":"string","default":""},"buttonText":{"type":"string","default":"Search"}}},"core\/tag-cloud":{"attributes":{"taxonomy":{"type":"string","default":"post_tag"},"className":{"type":"string"},"showTagCounts":{"type":"boolean","default":false},"align":{"type":"string"}}}}
\ No newline at end of file
From 8c82d3e2a8b6f553c62d38f38705ceac47e420e9 Mon Sep 17 00:00:00 2001
From: Paul Sealock
Date: Tue, 5 Mar 2019 22:48:20 +1300
Subject: [PATCH 072/169] Component: date-time: Remove comparison to now when
testing getMomentDate (#14230)
---
packages/components/src/date-time/test/date.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/packages/components/src/date-time/test/date.js b/packages/components/src/date-time/test/date.js
index a12999dbbc784..cd1b1e1e12e2b 100644
--- a/packages/components/src/date-time/test/date.js
+++ b/packages/components/src/date-time/test/date.js
@@ -29,7 +29,6 @@ describe( 'DatePicker', () => {
const wrapper = shallow( );
const date = wrapper.children().props().date;
expect( moment.isMoment( date ) ).toBe( true );
- expect( date.isSame( moment(), 'second' ) ).toBe( true );
} );
describe( 'getMomentDate', () => {
@@ -55,7 +54,6 @@ describe( 'DatePicker', () => {
const momentDate = wrapper.instance().getMomentDate();
expect( moment.isMoment( momentDate ) ).toBe( true );
- expect( momentDate.isSame( moment(), 'second' ) ).toBe( true );
} );
} );
From 5ebdc02b30839de857f17902a86c81d5761ac9f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ella=20van=C2=A0Durpe?=
Date: Tue, 5 Mar 2019 11:36:36 +0100
Subject: [PATCH 073/169] RichText: Fix undo after pattern (#13917)
* Fix undo after pattern
* Update e2e test
---
.../blocks/__snapshots__/list.test.js.snap | 2 +-
.../editor/src/components/rich-text/index.js | 21 ++++++++++++-------
.../src/components/rich-text/patterns.js | 5 +----
3 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap
index efc3b6d274373..119fce765c5f4 100644
--- a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap
+++ b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap
@@ -76,7 +76,7 @@ exports[`List can be created by using an asterisk at the start of a paragraph bl
exports[`List can undo asterisk transform 1`] = `
"
-1.
+1.
"
`;
diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js
index d6be0a622307e..3ca992296e4fb 100644
--- a/packages/editor/src/components/rich-text/index.js
+++ b/packages/editor/src/components/rich-text/index.js
@@ -133,9 +133,7 @@ export class RichText extends Component {
this.savedContent = value;
this.patterns = getPatterns( {
onReplace,
- onCreateUndoLevel: this.onCreateUndoLevel,
valueToFormat: this.valueToFormat,
- onChange: this.onChange,
} );
this.enterPatterns = getBlockTransforms( 'from' )
.filter( ( { type } ) => type === 'enter' );
@@ -395,10 +393,7 @@ export class RichText extends Component {
}
let { selectedFormat } = this.state;
- const { formats, text, start, end } = this.patterns.reduce(
- ( accumlator, transform ) => transform( accumlator ),
- this.createRecord()
- );
+ const { formats, text, start, end } = this.createRecord();
if ( this.formatPlaceholder ) {
formats[ this.state.start ] = formats[ this.state.start ] || [];
@@ -420,10 +415,22 @@ export class RichText extends Component {
delete formats[ this.state.start ];
}
- this.onChange( { formats, text, start, end, selectedFormat }, {
+ const change = { formats, text, start, end, selectedFormat };
+
+ this.onChange( change, {
withoutHistory: true,
} );
+ const transformed = this.patterns.reduce(
+ ( accumlator, transform ) => transform( accumlator ),
+ change
+ );
+
+ if ( transformed !== change ) {
+ this.onCreateUndoLevel();
+ this.onChange( { ...transformed, selectedFormat } );
+ }
+
// Create an undo level when input stops for over a second.
this.props.clearTimeout( this.onInput.timeout );
this.onInput.timeout = this.props.setTimeout( this.onCreateUndoLevel, 1000 );
diff --git a/packages/editor/src/components/rich-text/patterns.js b/packages/editor/src/components/rich-text/patterns.js
index a9e9a8010149d..a1d5794cca828 100644
--- a/packages/editor/src/components/rich-text/patterns.js
+++ b/packages/editor/src/components/rich-text/patterns.js
@@ -10,7 +10,7 @@ import {
slice,
} from '@wordpress/rich-text';
-export function getPatterns( { onReplace, valueToFormat, onCreateUndoLevel, onChange } ) {
+export function getPatterns( { onReplace, valueToFormat } ) {
const prefixTransforms = getBlockTransforms( 'from' )
.filter( ( { type } ) => type === 'prefix' );
@@ -40,7 +40,6 @@ export function getPatterns( { onReplace, valueToFormat, onCreateUndoLevel, onCh
const content = valueToFormat( slice( record, start, text.length ) );
const block = transformation.transform( content );
- onCreateUndoLevel();
onReplace( [ block ] );
return record;
@@ -70,8 +69,6 @@ export function getPatterns( { onReplace, valueToFormat, onCreateUndoLevel, onCh
return record;
}
- onChange( record );
-
record = remove( record, startIndex, startIndex + 1 );
record = remove( record, endIndex, endIndex + 1 );
record = applyFormat( record, { type: 'code' }, startIndex, endIndex );
From 53e86ee99bb1816470291b9e942cd79224eba749 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ella=20van=C2=A0Durpe?=
Date: Tue, 5 Mar 2019 11:40:39 +0100
Subject: [PATCH 074/169] RichText: don't use DOM to add line padding (#13850)
* RichText: don't use DOM to add line padding
* Clean up
* Keep track of user inserted line breaks
* Mark isEditableTree param unstable
---
.../__snapshots__/writing-flow.test.js.snap | 4 +-
.../blocks/__snapshots__/list.test.js.snap | 4 +-
.../editor/src/components/rich-text/index.js | 1 +
packages/rich-text/src/create.js | 30 +++++---------
packages/rich-text/src/insert-line-break.js | 24 ++---------
.../src/test/__snapshots__/to-dom.js.snap | 37 +++++++++++++----
packages/rich-text/src/test/helpers/index.js | 8 ++--
packages/rich-text/src/to-dom.js | 37 -----------------
packages/rich-text/src/to-tree.js | 40 ++++++++++++++++++-
9 files changed, 91 insertions(+), 94 deletions(-)
diff --git a/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap b/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap
index 0d02a8e655bfa..dca0ce4763cdb 100644
--- a/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap
+++ b/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap
@@ -72,7 +72,7 @@ exports[`adding blocks should create valid paragraph blocks when rapidly pressin
exports[`adding blocks should insert line break at end 1`] = `
"
-a
+a
"
`;
@@ -90,7 +90,7 @@ exports[`adding blocks should insert line break at start 1`] = `
exports[`adding blocks should insert line break in empty container 1`] = `
"
-
+
"
`;
diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap
index 119fce765c5f4..b2e8ec4875aac 100644
--- a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap
+++ b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap
@@ -146,13 +146,13 @@ exports[`List should indent and outdent level 2 3`] = `
exports[`List should insert a line break on shift+enter 1`] = `
"
-
+
"
`;
exports[`List should insert a line break on shift+enter in a non trailing list item 1`] = `
"
-
+
"
`;
diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js
index 3ca992296e4fb..e72a70b61e46c 100644
--- a/packages/editor/src/components/rich-text/index.js
+++ b/packages/editor/src/components/rich-text/index.js
@@ -193,6 +193,7 @@ export class RichText extends Component {
multilineTag: this.multilineTag,
multilineWrapperTags: this.multilineWrapperTags,
prepareEditableTree: this.props.prepareEditableTree,
+ __unstableIsEditableTree: true,
} );
}
diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js
index 90d54c82c4561..82348bbd4fa8a 100644
--- a/packages/rich-text/src/create.js
+++ b/packages/rich-text/src/create.js
@@ -108,6 +108,7 @@ export function create( {
range,
multilineTag,
multilineWrapperTags,
+ __unstableIsEditableTree: isEditableTree,
} = {} ) {
if ( typeof text === 'string' && text.length > 0 ) {
return {
@@ -128,6 +129,7 @@ export function create( {
return createFromElement( {
element,
range,
+ isEditableTree,
} );
}
@@ -136,6 +138,7 @@ export function create( {
range,
multilineTag,
multilineWrapperTags,
+ isEditableTree,
} );
}
@@ -257,6 +260,7 @@ function createFromElement( {
multilineTag,
multilineWrapperTags,
currentWrapperTags = [],
+ isEditableTree,
} ) {
const accumulator = createEmptyValue();
@@ -291,7 +295,10 @@ function createFromElement( {
continue;
}
- if ( node.getAttribute( 'data-rich-text-padding' ) ) {
+ if (
+ node.getAttribute( 'data-rich-text-padding' ) ||
+ ( isEditableTree && type === 'br' && ! node.getAttribute( 'data-rich-text-line-break' ) )
+ ) {
accumulateSelection( accumulator, node, range, createEmptyValue() );
continue;
}
@@ -433,7 +440,7 @@ function createFromMultilineElement( {
continue;
}
- let value = createFromElement( {
+ const value = createFromElement( {
element: node,
range,
multilineTag,
@@ -441,23 +448,6 @@ function createFromMultilineElement( {
currentWrapperTags,
} );
- // If a line consists of one single line break (invisible), consider the
- // line empty, wether this is the browser's doing or not.
- if ( value.text === '\n' ) {
- const start = value.start;
- const end = value.end;
-
- value = createEmptyValue();
-
- if ( start !== undefined ) {
- value.start = 0;
- }
-
- if ( end !== undefined ) {
- value.end = 0;
- }
- }
-
// Multiline value text should be separated by a double line break.
if ( index !== 0 || currentWrapperTags.length > 0 ) {
const formats = currentWrapperTags.length > 0 ? [ currentWrapperTags ] : [ , ];
@@ -495,7 +485,7 @@ function getAttributes( { element } ) {
for ( let i = 0; i < length; i++ ) {
const { name, value } = element.attributes[ i ];
- if ( name === 'data-rich-text-format-boundary' ) {
+ if ( name.indexOf( 'data-rich-text-' ) === 0 ) {
continue;
}
diff --git a/packages/rich-text/src/insert-line-break.js b/packages/rich-text/src/insert-line-break.js
index 540214d614f3c..08b0b70b864e6 100644
--- a/packages/rich-text/src/insert-line-break.js
+++ b/packages/rich-text/src/insert-line-break.js
@@ -3,32 +3,14 @@
*/
import { insert } from './insert';
-import { LINE_SEPARATOR } from './special-characters';
/**
- * Inserts a line break at the given or selected position. Inserts two line
- * breaks if at the end of a line.
+ * Inserts a line break at the given or selected position.
*
* @param {Object} value Value to modify.
*
- * @return {Object} The value with the line break(s) inserted.
+ * @return {Object} The value with the line break inserted.
*/
export function insertLineBreak( value ) {
- const { text, end } = value;
- const length = text.length;
-
- let toInsert = '\n';
-
- // If the caret is at the end of the text, and there is no
- // trailing line break or no text at all, we have to insert two
- // line breaks in order to create a new line visually and place
- // the caret there.
- if (
- ( end === length || text[ end ] === LINE_SEPARATOR ) &&
- ( text[ end - 1 ] !== '\n' || length === 0 )
- ) {
- toInsert = '\n\n';
- }
-
- return insert( value, toInsert );
+ return insert( value, '\n' );
}
diff --git a/packages/rich-text/src/test/__snapshots__/to-dom.js.snap b/packages/rich-text/src/test/__snapshots__/to-dom.js.snap
index 78378090137bd..a68cdab1c911e 100644
--- a/packages/rich-text/src/test/__snapshots__/to-dom.js.snap
+++ b/packages/rich-text/src/test/__snapshots__/to-dom.js.snap
@@ -132,8 +132,13 @@ exports[`recordToDom should filter format boundary attributes 1`] = `
exports[`recordToDom should handle br 1`] = `
-
+
+
`;
@@ -143,17 +148,24 @@ exports[`recordToDom should handle br with formatting 1`] = `
data-rich-text-format-boundary="true"
>
-
+
+
te
-
+
st
a
-
+
-
+
b