diff --git a/.eslintrc.js b/.eslintrc.js index b1a6b834b53aff..43e259b40c7b7e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -123,7 +123,7 @@ module.exports = { '**/*.@(android|ios|native).js', '**/benchmark/**/*.js', '**/@(__mocks__|__tests__|test)/**/*.js', - '**/storybook/**/*.js', + '**/@(storybook|stories)/**/*.js', ], }, { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33d5a864d489d8..c6d9694eae9b62 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -40,7 +40,7 @@ /packages/custom-templated-path-webpack-plugin @ntwb @nerrad @ajitbohra /packages/docgen @nosolosw /packages/e2e-test-utils @gziolo @ntwb @nerrad @ajitbohra -/packages/e2e-tests @gziolo @ntwb @nerrad @ajitbohra @talldan +/packages/e2e-tests @ntwb @nerrad @ajitbohra @talldan /packages/eslint-plugin @gziolo @ntwb @nerrad @ajitbohra /packages/jest-console @gziolo @ntwb @nerrad @ajitbohra /packages/jest-preset-default @gziolo @ntwb @nerrad @ajitbohra @@ -51,9 +51,9 @@ /packages/scripts @youknowriad @gziolo @ntwb @nerrad @ajitbohra # UI Components -/packages/components @youknowriad @gziolo @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @chrisvanpatten -/packages/compose @youknowriad @gziolo @ajitbohra @jaymanpandya @jorgefilipecosta @talldan -/packages/element @youknowriad @gziolo @ajitbohra @jaymanpandya @jorgefilipecosta @talldan +/packages/components @youknowriad @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @chrisvanpatten +/packages/compose @youknowriad @ajitbohra @jaymanpandya @jorgefilipecosta @talldan +/packages/element @youknowriad @ajitbohra @jaymanpandya @jorgefilipecosta @talldan /packages/notices @ajitbohra @jaymanpandya @jorgefilipecosta @talldan /packages/nux @ajitbohra @jaymanpandya @jorgefilipecosta @talldan @noisysocks /packages/viewport @youknowriad @ajitbohra @jaymanpandya @jorgefilipecosta @talldan diff --git a/.npmpackagejsonlintrc.json b/.npmpackagejsonlintrc.json new file mode 100644 index 00000000000000..daab51fa30d456 --- /dev/null +++ b/.npmpackagejsonlintrc.json @@ -0,0 +1,39 @@ +{ + "extends": "@wordpress/npm-package-json-lint-config", + "rules": { + "description-format": [ + "error", + { + "requireCapitalFirstLetter": true, + "requireEndingPeriod": true + } + ], + "prefer-no-devDependencies": "error", + "require-publishConfig": "error", + "require-repository-directory": "error", + "valid-values-author": [ + "error", + [ + "The WordPress Contributors" + ] + ], + "valid-values-publishConfig": [ + "error", + [ + { + "access": "public" + } + ] + ] + }, + "overrides": [ + { + "patterns": [ "./package.json" ], + "rules": { + "require-publishConfig": "off", + "require-repository-directory": "off", + "prefer-no-devDependencies": "off" + } + } + ] +} diff --git a/.travis.yml b/.travis.yml index 27b1e7383e85ff..db9acbf2d2174b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,8 @@ branches: only: - master - rnmobile/master - - wp/trunk + - rnmobile/releases + - /wp\/.*/ env: global: diff --git a/bin/commander.js b/bin/commander.js index dcc863872f034d..95cfc56b25efd9 100755 --- a/bin/commander.js +++ b/bin/commander.js @@ -17,7 +17,7 @@ const uuid = require( 'uuid/v4' ); // Config const gitRepoOwner = 'WordPress'; -const gitRepoURL = 'git@github.com:' + gitRepoOwner + '/gutenberg.git'; +const gitRepoURL = 'https://github.com/' + gitRepoOwner + '/gutenberg.git'; const svnRepoURL = 'https://plugins.svn.wordpress.org/gutenberg'; // Working Directories @@ -95,6 +95,7 @@ function runShellScript( script, cwd ) { env: { NO_CHECKS: true, PATH: process.env.PATH, + HOME: process.env.HOME, }, stdio: [ 'inherit', 'ignore', 'inherit' ], } ); diff --git a/bin/get-vendor-scripts.php b/bin/get-vendor-scripts.php index 442e77b0eb4358..e7295f556b4cb2 100755 --- a/bin/get-vendor-scripts.php +++ b/bin/get-vendor-scripts.php @@ -32,4 +32,13 @@ function wp_add_inline_script() {} require_once dirname( dirname( __FILE__ ) ) . '/lib/client-assets.php'; -gutenberg_register_vendor_scripts(); +/** + * Hi, phpcs + */ +function run_gutenberg_register_vendor_scripts() { + global $wp_scripts; + + gutenberg_register_vendor_scripts( $wp_scripts ); +} + +run_gutenberg_register_vendor_scripts(); diff --git a/bin/packages/build-worker.js b/bin/packages/build-worker.js index 7e3e636c013a1d..d0f1d1c809984a 100644 --- a/bin/packages/build-worker.js +++ b/bin/packages/build-worker.js @@ -94,7 +94,7 @@ const BUILD_TASK_BY_EXTENSION = { const builtSass = await renderSass( { file, - includePaths: [ path.resolve( __dirname, '../../assets/stylesheets' ) ], + includePaths: [ path.join( PACKAGES_DIR, 'base-styles' ) ], data: ( [ 'colors', diff --git a/bin/packages/post-css-config.js b/bin/packages/post-css-config.js index 3d7861f75044bd..842688a8d784b7 100644 --- a/bin/packages/post-css-config.js +++ b/bin/packages/post-css-config.js @@ -1,64 +1,7 @@ +const { adminColorSchemes } = require( '@wordpress/base-styles' ); + module.exports = [ - require( '@wordpress/postcss-themes' )( { - defaults: { - primary: '#0085ba', - secondary: '#11a0d2', - toggle: '#11a0d2', - button: '#007cba', - outlines: '#007cba', - }, - themes: { - 'admin-color-light': { - primary: '#0085ba', - secondary: '#c75726', - toggle: '#11a0d2', - button: '#0085ba', - outlines: '#007cba', - }, - 'admin-color-blue': { - primary: '#82b4cb', - secondary: '#d9ab59', - toggle: '#82b4cb', - button: '#d9ab59', - outlines: '#417e9B', - }, - 'admin-color-coffee': { - primary: '#c2a68c', - secondary: '#9fa47b', - toggle: '#c2a68c', - button: '#c2a68c', - outlines: '#59524c', - }, - 'admin-color-ectoplasm': { - primary: '#a7b656', - secondary: '#c77430', - toggle: '#a7b656', - button: '#a7b656', - outlines: '#523f6d', - }, - 'admin-color-midnight': { - primary: '#e14d43', - secondary: '#77a6b9', - toggle: '#77a6b9', - button: '#e14d43', - outlines: '#497b8d', - }, - 'admin-color-ocean': { - primary: '#a3b9a2', - secondary: '#a89d8a', - toggle: '#a3b9a2', - button: '#a3b9a2', - outlines: '#5e7d5e', - }, - 'admin-color-sunrise': { - primary: '#d1864a', - secondary: '#c8b03c', - toggle: '#c8b03c', - button: '#d1864a', - outlines: '#837425', - }, - }, - } ), + require( '@wordpress/postcss-themes' )( adminColorSchemes ), require( 'autoprefixer' )( { grid: true } ), require( 'postcss-color-function' ), ]; diff --git a/docs/contributors/coding-guidelines.md b/docs/contributors/coding-guidelines.md index 2e02dfeaa10f7e..8e69e8c8ccddbd 100644 --- a/docs/contributors/coding-guidelines.md +++ b/docs/contributors/coding-guidelines.md @@ -59,7 +59,7 @@ export default function Notice( { children, onRemove, isDismissible } ) { } ``` -A component's class name should **never** be used outside its own folder (with rare exceptions such as [`_z-index.scss`](https://github.com/WordPress/gutenberg/blob/master/assets/stylesheets/_z-index.scss)). If you need to inherit styles of another component in your own components, you should render an instance of that other component. At worst, you should duplicate the styles within your own component's stylesheet. This is intended to improve maintainability by treating individual components as the isolated abstract interface. +A component's class name should **never** be used outside its own folder (with rare exceptions such as [`_z-index.scss`](https://github.com/WordPress/gutenberg/blob/master/packages/base-styles/_z-index.scss)). If you need to inherit styles of another component in your own components, you should render an instance of that other component. At worst, you should duplicate the styles within your own component's stylesheet. This is intended to improve maintainability by treating individual components as the isolated abstract interface. #### SCSS File Naming Conventions for Blocks diff --git a/docs/contributors/git-workflow.md b/docs/contributors/git-workflow.md index 7f1499bc7de34a..5fe7a79d556fa3 100644 --- a/docs/contributors/git-workflow.md +++ b/docs/contributors/git-workflow.md @@ -36,12 +36,23 @@ To sum it up, you need to fetch any new changes in the repository, rebase your b ```sh git fetch git rebase master -git push --force-with-lease your-branch-name +git push --force-with-lease origin your-branch-name ``` ## Keeping Your Fork Up To Date -Working on pull request starts with forking the Gutenberg repository, your separate working copy. Which can easily go out of sync as new pull requests are merged into the main repository. Here your working repository is a `fork` and the main Gutenberg repository is `upstream`. When working on new pull request you should always update your fork before you do `git checkout -b my-new-branch` to work on a feature or fix. +Working on pull request starts with forking the Gutenberg repository, your separate working copy. Which can easily go out of sync as new pull requests are merged into the main repository. Here your working repository is a `fork` and the main Gutenberg repository is `upstream`. When working on new pull request you should always update your fork before you do `git checkout -b my-new-branch` to work on a feature or fix. + +You will need to add an `upstream` remote in order to keep your fork updated. + +```sh +git remote add origin upstream https://github.com/WordPress/gutenberg.git +git remote -v +origin git@github.com:your-account/gutenberg.git (fetch) +origin git@github.com:your-account/gutenberg.git (push) +upstream https://github.com/WordPress/gutenberg.git (fetch) +upstream https://github.com/WordPress/gutenberg.git (push) +``` To sync your fork you need to fetch the upstream changes and merge them into your fork. These are the corresponding commands: @@ -57,7 +68,7 @@ This will update you local copy to update your fork on github push your changes git push ``` -The above commands will update your `master` branch from _upstream_. To update any other branch replace `master` with the respective branch name. +The above commands will update your `master` branch from _upstream_. To update any other branch replace `master` with the respective branch name. ## References 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 eb9201165dafd6..ac50b42d83b114 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -189,6 +189,19 @@ _Returns_ - `Array`: Ordered client IDs of editor blocks. +# **getBlockParents** + +Given a block client ID, returns the list of all its parents from top to bottom. + +_Parameters_ + +- _state_ `Object`: Editor state. +- _clientId_ `string`: Block from which to find root client ID. + +_Returns_ + +- `Array`: ClientIDs of the parent blocks. + # **getBlockRootClientId** Given a block client ID, returns the root block from which the block is @@ -345,7 +358,7 @@ _Parameters_ _Returns_ -- `Array`: Items that appear in inserter. +- `Array`: Items that appear in inserter. # **getLastMultiSelectedBlockClientId** diff --git a/docs/designers-developers/developers/data/data-core-edit-post.md b/docs/designers-developers/developers/data/data-core-edit-post.md index 93efda1bd0bc4f..49801a69817be8 100644 --- a/docs/designers-developers/developers/data/data-core-edit-post.md +++ b/docs/designers-developers/developers/data/data-core-edit-post.md @@ -80,11 +80,11 @@ _Parameters_ - _state_ `Object`: Global application state. - _preferenceKey_ `string`: Preference Key. -- _defaultValue_ `Mixed`: Default Value. +- _defaultValue_ `*`: Default Value. _Returns_ -- `Mixed`: Preference Value. +- `*`: Preference Value. # **getPreferences** diff --git a/docs/designers-developers/developers/data/data-core-nux.md b/docs/designers-developers/developers/data/data-core-nux.md index e937601ec864b6..92dcf6be1d0ac2 100644 --- a/docs/designers-developers/developers/data/data-core-nux.md +++ b/docs/designers-developers/developers/data/data-core-nux.md @@ -30,7 +30,7 @@ _Parameters_ _Returns_ -- `?NUX.GuideInfo`: Information about the associated guide. +- `?NUXGuideInfo`: Information about the associated guide. # **isTipVisible** diff --git a/docs/designers-developers/developers/data/data-core.md b/docs/designers-developers/developers/data/data-core.md index ce0cda7d3542a3..79f3f41991c08f 100644 --- a/docs/designers-developers/developers/data/data-core.md +++ b/docs/designers-developers/developers/data/data-core.md @@ -512,7 +512,7 @@ a given URl has been received. _Parameters_ - _url_ `string`: URL to preview the embed for. -- _preview_ `Mixed`: Preview data. +- _preview_ `*`: Preview data. _Returns_ diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md index 77496681ce4847..81794c30be53fb 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md @@ -107,4 +107,6 @@ Once a block is registered, you should immediately see that it becomes available A block name must be prefixed with a namespace specific to your plugin. This helps prevent conflicts when more than one plugin registers a block with the same name. In this example, the namespace is `gutenberg-examples`. +Block names _must_ include only lowercase alphanumeric characters or dashes and start with a letter. Example: `my-plugin/my-custom-block`. + The `edit` and `save` functions describe the structure of your block in the context of the editor and the saved content respectively. While the difference is not obvious in this simple example, in the following sections we'll explore how these are used to enable customization of the block in the editor preview. diff --git a/docs/manifest-devhub.json b/docs/manifest-devhub.json index 10b2c9362e8aef..a8d1a369642ee2 100644 --- a/docs/manifest-devhub.json +++ b/docs/manifest-devhub.json @@ -641,6 +641,12 @@ "markdown_source": "../packages/components/src/date-time/README.md", "parent": "components" }, + { + "title": "DimensionControl", + "slug": "dimension-control", + "markdown_source": "../packages/components/src/dimension-control/README.md", + "parent": "components" + }, { "title": "Disabled", "slug": "disabled", @@ -971,6 +977,12 @@ "markdown_source": "../packages/components/src/tree-select/README.md", "parent": "components" }, + { + "title": "VisuallyHidden", + "slug": "visually-hidden", + "markdown_source": "../packages/components/src/visually-hidden/README.md", + "parent": "components" + }, { "title": "Data Module Reference", "slug": "data", @@ -1079,6 +1091,12 @@ "markdown_source": "../packages/babel-preset-default/README.md", "parent": "packages" }, + { + "title": "@wordpress/base-styles", + "slug": "packages-base-styles", + "markdown_source": "../packages/base-styles/README.md", + "parent": "packages" + }, { "title": "@wordpress/blob", "slug": "packages-blob", diff --git a/gutenberg.php b/gutenberg.php index 53333e55831917..9e80dee2f6a1aa 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: 6.6.0 + * Version: 6.8.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/block-directory.php b/lib/block-directory.php new file mode 100644 index 00000000000000..723db66de2b3c5 --- /dev/null +++ b/lib/block-directory.php @@ -0,0 +1,21 @@ + 'core/search', 'social-link.php' => gutenberg_get_registered_social_link_blocks(), 'tag-cloud.php' => 'core/tag-cloud', + 'site-title.php' => 'core/site-title', ); $registry = WP_Block_Type_Registry::get_instance(); diff --git a/lib/client-assets.php b/lib/client-assets.php index 327e2f436897f2..6906d39996a4b1 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -42,6 +42,7 @@ function gutenberg_url( $path ) { * * @since 4.1.0 * + * @param WP_Scripts $scripts WP_Scripts instance (passed by reference). * @param string $handle Name of the script. Should be unique. * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory. * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array. @@ -52,10 +53,8 @@ function gutenberg_url( $path ) { * @param bool $in_footer Optional. Whether to enqueue the script before instead of in the . * Default 'false'. */ -function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) { - global $wp_scripts; - - $script = $wp_scripts->query( $handle, 'registered' ); +function gutenberg_override_script( &$scripts, $handle, $src, $deps = array(), $ver = false, $in_footer = false ) { + $script = $scripts->query( $handle, 'registered' ); if ( $script ) { /* * In many ways, this is a reimplementation of `wp_register_script` but @@ -67,6 +66,7 @@ function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false $script->src = $src; $script->deps = $deps; $script->ver = $ver; + $script->args = $in_footer; /* * The script's `group` designation is an indication of whether it is @@ -81,7 +81,7 @@ function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false $script->add_data( 'group', 1 ); } } else { - wp_register_script( $handle, $src, $deps, $ver, $in_footer ); + $scripts->add( $handle, $src, $deps, $ver, $in_footer ); } /* @@ -93,7 +93,7 @@ function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false * See: https://core.trac.wordpress.org/ticket/46089 */ if ( 'wp-i18n' !== $handle && 'wp-polyfill' !== $handle ) { - wp_set_script_translations( $handle, 'default' ); + $scripts->set_translations( $handle, 'default' ); } } @@ -155,6 +155,7 @@ function gutenberg_override_translation_file( $file, $handle ) { * * @since 4.1.0 * + * @param WP_Styles $styles WP_Styles instance (passed by reference). * @param string $handle Name of the stylesheet. Should be unique. * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array. @@ -166,18 +167,69 @@ function gutenberg_override_translation_file( $file, $handle ) { * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like * '(orientation: portrait)' and '(max-width: 640px)'. */ -function gutenberg_override_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { - wp_deregister_style( $handle ); - wp_register_style( $handle, $src, $deps, $ver, $media ); +function gutenberg_override_style( &$styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { + $style = $styles->query( $handle, 'registered' ); + if ( $style ) { + $styles->remove( $handle ); + } + $styles->add( $handle, $src, $deps, $ver, $media ); +} + +/** + * Registers vendor JavaScript files to be used as dependencies of the editor + * and plugins. + * + * This function is called from a script during the plugin build process, so it + * should not call any WordPress PHP functions. + * + * @since 0.1.0 + * + * @param WP_Scripts $scripts WP_Scripts instance (passed by reference). + */ +function gutenberg_register_vendor_scripts( &$scripts ) { + $suffix = SCRIPT_DEBUG ? '' : '.min'; + + // Vendor Scripts. + $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix; + + // TODO: Overrides for react, react-dom and lodash are necessary + // until WordPress 5.3 is released. + gutenberg_register_vendor_script( + $scripts, + 'react', + 'https://unpkg.com/react@16.9.0/umd/react' . $react_suffix . '.js', + array( 'wp-polyfill' ), + '16.9.0', + true + ); + gutenberg_register_vendor_script( + $scripts, + 'react-dom', + 'https://unpkg.com/react-dom@16.9.0/umd/react-dom' . $react_suffix . '.js', + array( 'react' ), + '16.9.0', + true + ); + gutenberg_register_vendor_script( + $scripts, + 'lodash', + 'https://unpkg.com/lodash@4.17.15/lodash' . $suffix . '.js', + array(), + '4.17.15', + true + ); } +add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' ); /** * Registers all the WordPress packages scripts that are in the standardized * `build/` location. * * @since 4.5.0 + * + * @param WP_Scripts $scripts WP_Scripts instance (passed by reference). */ -function gutenberg_register_packages_scripts() { +function gutenberg_register_packages_scripts( &$scripts ) { foreach ( glob( gutenberg_dir_path() . 'build/*/index.js' ) as $path ) { // Prefix `wp-` to package directory to get script handle. // For example, `…/build/a11y/index.js` becomes `wp-a11y`. @@ -206,6 +258,7 @@ function gutenberg_register_packages_scripts() { $gutenberg_path = substr( $path, strlen( gutenberg_dir_path() ) ); gutenberg_override_script( + $scripts, $handle, gutenberg_url( $gutenberg_path ), $dependencies, @@ -214,116 +267,74 @@ function gutenberg_register_packages_scripts() { ); } } +add_action( 'wp_default_scripts', 'gutenberg_register_packages_scripts' ); /** - * Registers common scripts and styles to be used as dependencies of the editor - * and plugins. + * Registers all the WordPress packages styles that are in the standardized + * `build/` location. * - * @since 0.1.0 - */ -function gutenberg_register_scripts_and_styles() { - global $wp_scripts; - - gutenberg_register_vendor_scripts(); - gutenberg_register_packages_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 . - if ( isset( $wp_scripts->registered['wp-api-fetch'] ) ) { - $wp_scripts->registered['wp-api-fetch']->deps[] = 'wp-hooks'; - } - wp_add_inline_script( - 'wp-api-fetch', - sprintf( - 'wp.apiFetch.nonceMiddleware = wp.apiFetch.createNonceMiddleware( "%s" );' . - 'wp.apiFetch.use( wp.apiFetch.nonceMiddleware );' . - 'wp.apiFetch.nonceEndpoint = "%s";', - ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' ), - admin_url( 'admin-ajax.php?action=gutenberg_rest_nonce' ) - ), - 'after' - ); - - // TEMPORARY: Core does not (yet) provide persistence migration from the - // introduction of the block editor and still calls the data plugins. - // We unset the existing inline scripts first. - $wp_scripts->registered['wp-data']->extra['after'] = array(); - wp_add_inline_script( - 'wp-data', - implode( - "\n", - array( - '( function() {', - ' var userId = ' . get_current_user_ID() . ';', - ' var storageKey = "WP_DATA_USER_" + userId;', - ' wp.data', - ' .use( wp.data.plugins.persistence, { storageKey: storageKey } );', - ' wp.data.plugins.persistence.__unstableMigrate( { storageKey: storageKey } );', - '} )();', - ) - ) - ); + * @since 6.7.0 + * @param WP_Styles $styles WP_Styles instance (passed by reference). + */ +function gutenberg_register_packages_styles( &$styles ) { // Editor Styles. - // This empty stylesheet is defined to ensure backward compatibility. - gutenberg_override_style( 'wp-blocks', false ); - gutenberg_override_style( + $styles, 'wp-block-editor', gutenberg_url( 'build/block-editor/style.css' ), array( 'wp-components', 'wp-editor-font' ), filemtime( gutenberg_dir_path() . 'build/editor/style.css' ) ); - wp_style_add_data( 'wp-block-editor', 'rtl', 'replace' ); + $styles->add_data( 'wp-block-editor', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-editor', gutenberg_url( 'build/editor/style.css' ), - array( 'wp-components', 'wp-block-editor', 'wp-nux', 'wp-block-directory' ), + array( 'wp-components', 'wp-block-editor', 'wp-nux' ), filemtime( gutenberg_dir_path() . 'build/editor/style.css' ) ); - wp_style_add_data( 'wp-editor', 'rtl', 'replace' ); + $styles->add_data( 'wp-editor', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-edit-post', gutenberg_url( 'build/edit-post/style.css' ), array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ), filemtime( gutenberg_dir_path() . 'build/edit-post/style.css' ) ); - wp_style_add_data( 'wp-edit-post', 'rtl', 'replace' ); + $styles->add_data( 'wp-edit-post', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-components', gutenberg_url( 'build/components/style.css' ), array(), filemtime( gutenberg_dir_path() . 'build/components/style.css' ) ); - wp_style_add_data( 'wp-components', 'rtl', 'replace' ); + $styles->add_data( 'wp-components', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-block-library', gutenberg_url( 'build/block-library/style.css' ), array(), filemtime( gutenberg_dir_path() . 'build/block-library/style.css' ) ); - wp_style_add_data( 'wp-block-library', 'rtl', 'replace' ); + $styles->add_data( 'wp-block-library', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-format-library', gutenberg_url( 'build/format-library/style.css' ), array( 'wp-block-editor', 'wp-components' ), filemtime( gutenberg_dir_path() . 'build/format-library/style.css' ) ); - wp_style_add_data( 'wp-format-library', 'rtl', 'replace' ); + $styles->add_data( 'wp-format-library', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-edit-blocks', gutenberg_url( 'build/block-library/editor.css' ), array( @@ -335,92 +346,107 @@ function gutenberg_register_scripts_and_styles() { ), filemtime( gutenberg_dir_path() . 'build/block-library/editor.css' ) ); - wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' ); + $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-nux', gutenberg_url( 'build/nux/style.css' ), array( 'wp-components' ), filemtime( gutenberg_dir_path() . 'build/nux/style.css' ) ); - wp_style_add_data( 'wp-nux', 'rtl', 'replace' ); + $styles->add_data( 'wp-nux', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-block-library-theme', gutenberg_url( 'build/block-library/theme.css' ), array(), filemtime( gutenberg_dir_path() . 'build/block-library/theme.css' ) ); - wp_style_add_data( 'wp-block-library-theme', 'rtl', 'replace' ); + $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-list-reusable-blocks', gutenberg_url( 'build/list-reusable-blocks/style.css' ), array( 'wp-components' ), filemtime( gutenberg_dir_path() . 'build/list-reusable-blocks/style.css' ) ); - wp_style_add_data( 'wp-list-reusable-block', 'rtl', 'replace' ); + $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-edit-widgets', gutenberg_url( 'build/edit-widgets/style.css' ), array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ), filemtime( gutenberg_dir_path() . 'build/edit-widgets/style.css' ) ); - wp_style_add_data( 'wp-edit-widgets', 'rtl', 'replace' ); + $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' ); gutenberg_override_style( + $styles, 'wp-block-directory', gutenberg_url( 'build/block-directory/style.css' ), - array( 'wp-components' ), + array( 'wp-block-editor', 'wp-components' ), filemtime( gutenberg_dir_path() . 'build/block-directory/style.css' ) ); - wp_style_add_data( 'wp-block-directory', 'rtl', 'replace' ); - - if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) { - $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD; - - wp_enqueue_script( - 'gutenberg-live-reload', - $live_reload_url - ); - } + $styles->add_data( 'wp-block-directory', 'rtl', 'replace' ); } -add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); -add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); +add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' ); /** - * Registers vendor JavaScript files to be used as dependencies of the editor + * Registers common scripts and styles to be used as dependencies of the editor * and plugins. * - * This function is called from a script during the plugin build process, so it - * should not call any WordPress PHP functions. - * * @since 0.1.0 */ -function gutenberg_register_vendor_scripts() { - $suffix = SCRIPT_DEBUG ? '' : '.min'; - - // Vendor Scripts. - $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix; +function gutenberg_enqueue_block_editor_assets() { + global $wp_scripts; - // TODO: Overrides for react, react-dom and lodash are necessary - // until WordPress 5.3 is released. - gutenberg_register_vendor_script( - 'react', - 'https://unpkg.com/react@16.9.0/umd/react' . $react_suffix . '.js', - array( 'wp-polyfill' ) - ); - gutenberg_register_vendor_script( - 'react-dom', - 'https://unpkg.com/react-dom@16.9.0/umd/react-dom' . $react_suffix . '.js', - array( 'react' ) + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.nonceMiddleware = wp.apiFetch.createNonceMiddleware( "%s" );' . + 'wp.apiFetch.use( wp.apiFetch.nonceMiddleware );' . + 'wp.apiFetch.nonceEndpoint = "%s";' . + 'wp.apiFetch.use( wp.apiFetch.mediaUploadMiddleware );', + ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' ), + admin_url( 'admin-ajax.php?action=gutenberg_rest_nonce' ) + ), + 'after' ); - gutenberg_register_vendor_script( - 'lodash', - 'https://unpkg.com/lodash@4.17.15/lodash' . $suffix . '.js' + + // TEMPORARY: Core does not (yet) provide persistence migration from the + // introduction of the block editor and still calls the data plugins. + // We unset the existing inline scripts first. + $wp_scripts->registered['wp-data']->extra['after'] = array(); + wp_add_inline_script( + 'wp-data', + implode( + "\n", + array( + '( function() {', + ' var userId = ' . get_current_user_ID() . ';', + ' var storageKey = "WP_DATA_USER_" + userId;', + ' wp.data', + ' .use( wp.data.plugins.persistence, { storageKey: storageKey } );', + ' wp.data.plugins.persistence.__unstableMigrate( { storageKey: storageKey } );', + '} )();', + ) + ) ); + + if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) { + $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD; + + wp_enqueue_script( + 'gutenberg-live-reload', + $live_reload_url + ); + } } +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_block_editor_assets' ); /** * Retrieves a unique and reasonably short and human-friendly filename for a @@ -458,14 +484,21 @@ function gutenberg_vendor_script_filename( $handle, $src ) { * possible, or downloading it if the cached version is unavailable or * outdated. * - * @param string $handle Name of the script. - * @param string $src Full URL of the external script. - * @param array $deps Optional. An array of registered script handles this - * script depends on. + * @param WP_Scripts $scripts WP_Scripts instance (passed by reference). + * @param string $handle Name of the script. + * @param string $src Full URL of the external script. + * @param array $deps Optional. An array of registered script handles this + * script depends on. + * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL + * as a query string for cache busting purposes. If version is set to false, a version + * number is automatically added equal to current installed WordPress version. + * If set to null, no version is added. + * @param bool $in_footer Optional. Whether to enqueue the script before instead of in the . + * Default 'false'. * * @since 0.1.0 */ -function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) { +function gutenberg_register_vendor_script( &$scripts, $handle, $src, $deps = array(), $ver = null, $in_footer = false ) { if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) { return; } @@ -495,7 +528,7 @@ function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) { if ( ! $f ) { // Failed to open the file for writing, probably due to server // permissions. Enqueue the script directly from the URL instead. - gutenberg_override_script( $handle, $src, $deps, null ); + gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer ); return; } fclose( $f ); @@ -508,16 +541,18 @@ function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) { // The request failed. If the file is already cached, continue to // use this file. If not, then unlink the 0 byte file, and enqueue // the script directly from the URL. - gutenberg_override_script( $handle, $src, $deps, null ); + gutenberg_override_script( $scripts, $handle, $src, $deps, $ver, $in_footer ); unlink( $full_path ); return; } } gutenberg_override_script( + $scripts, $handle, gutenberg_url( 'vendor/' . $filename ), $deps, - null + $ver, + $in_footer ); } diff --git a/lib/customizer.php b/lib/customizer.php index 771ced523f5c98..1f27db00ee3085 100644 --- a/lib/customizer.php +++ b/lib/customizer.php @@ -55,7 +55,7 @@ function gutenberg_customize_register( $wp_customize ) { 'sanitize_callback' => 'gutenberg_customize_sanitize', ) ); - if ( get_option( 'gutenberg-experiments' ) && array_key_exists( 'gutenberg-widget-experiments', get_option( 'gutenberg-experiments' ) ) ) { + if ( gutenberg_is_experiment_enabled( 'gutenberg-widget-experiments' ) ) { $wp_customize->add_section( 'gutenberg_widget_blocks', array( 'title' => __( 'Widget Blocks (Experimental)', 'gutenberg' ) ) diff --git a/lib/experiments-page.php b/lib/experiments-page.php index bbc04650a732e3..765e0cc5883d9d 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -130,12 +130,11 @@ function gutenberg_display_experiment_section() { * @return array Filtered editor settings. */ function gutenberg_experiments_editor_settings( $settings ) { - $experiments_exist = get_option( 'gutenberg-experiments' ); $experiments_settings = array( - '__experimentalEnableLegacyWidgetBlock' => $experiments_exist ? array_key_exists( 'gutenberg-widget-experiments', get_option( 'gutenberg-experiments' ) ) : false, - '__experimentalEnableMenuBlock' => $experiments_exist ? array_key_exists( 'gutenberg-menu-block', get_option( 'gutenberg-experiments' ) ) : false, - '__experimentalBlockDirectory' => $experiments_exist ? array_key_exists( 'gutenberg-block-directory', get_option( 'gutenberg-experiments' ) ) : false, - '__experimentalEnableFullSiteEditing' => $experiments_exist ? array_key_exists( 'gutenberg-full-site-editing', get_option( 'gutenberg-experiments' ) ) : false, + '__experimentalEnableLegacyWidgetBlock' => gutenberg_is_experiment_enabled( 'gutenberg-widget-experiments' ), + '__experimentalEnableMenuBlock' => gutenberg_is_experiment_enabled( 'gutenberg-menu-block' ), + '__experimentalBlockDirectory' => gutenberg_is_experiment_enabled( 'gutenberg-block-directory' ), + '__experimentalEnableFullSiteEditing' => gutenberg_is_experiment_enabled( 'gutenberg-full-site-editing' ), ); return array_merge( $settings, $experiments_settings ); diff --git a/lib/load.php b/lib/load.php index a0996f3b94f4ff..58e545b38a6f7a 100644 --- a/lib/load.php +++ b/lib/load.php @@ -9,6 +9,20 @@ die( 'Silence is golden.' ); } +/** + * Checks whether the Gutenberg experiment is enabled. + * + * @since 6.7.0 + * + * @param string $name The name of the experiment. + * + * @return bool True when the experiment is enabled. + */ +function gutenberg_is_experiment_enabled( $name ) { + $experiments = get_option( 'gutenberg-experiments' ); + return ! empty( $experiments[ $name ] ); +} + // These files only need to be loaded if within a rest server instance // which this class will exist if that is the case. if ( class_exists( 'WP_REST_Controller' ) ) { @@ -22,13 +36,12 @@ require dirname( __FILE__ ) . '/class-experimental-wp-widget-blocks-manager.php'; require dirname( __FILE__ ) . '/class-wp-rest-widget-areas-controller.php'; } - /** - * End: Include for phase 2 - */ - if ( ! class_exists( 'WP_REST_Block_Directory_Controller' ) ) { require dirname( __FILE__ ) . '/class-wp-rest-block-directory-controller.php'; } + /** + * End: Include for phase 2 + */ require dirname( __FILE__ ) . '/rest-api.php'; } @@ -41,7 +54,9 @@ require dirname( __FILE__ ) . '/blocks.php'; require dirname( __FILE__ ) . '/templates.php'; +require dirname( __FILE__ ) . '/template-loader.php'; require dirname( __FILE__ ) . '/client-assets.php'; +require dirname( __FILE__ ) . '/block-directory.php'; require dirname( __FILE__ ) . '/demo.php'; require dirname( __FILE__ ) . '/widgets.php'; require dirname( __FILE__ ) . '/widgets-page.php'; diff --git a/lib/rest-api.php b/lib/rest-api.php index 5aa85b6d83e48b..6ad5e6d0e6f0ef 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -85,6 +85,10 @@ function gutenberg_register_rest_widget_areas() { * @since 6.5.0 */ function gutenberg_register_rest_block_directory() { + if ( ! gutenberg_is_experiment_enabled( 'gutenberg-block-directory' ) ) { + return; + } + $block_directory_controller = new WP_REST_Block_Directory_Controller(); $block_directory_controller->register_routes(); } diff --git a/lib/template-canvas.php b/lib/template-canvas.php new file mode 100644 index 00000000000000..a0e0da7ea01752 --- /dev/null +++ b/lib/template-canvas.php @@ -0,0 +1,23 @@ + + +> + + + + + +> + + + + + + + diff --git a/lib/template-loader.php b/lib/template-loader.php new file mode 100644 index 00000000000000..1c6923091ce323 --- /dev/null +++ b/lib/template-loader.php @@ -0,0 +1,177 @@ + 'wp_template', + 'post_status' => 'publish', + 'post_name__in' => $slugs, + 'orderby' => 'post_name__in', + 'posts_per_page' => 1, + ) + ); + + if ( $template_query->have_posts() ) { + $template_posts = $template_query->get_posts(); + $_wp_current_template_post = array_shift( $template_posts ); + } + + // Add extra hooks for template canvas. + add_action( 'wp_head', 'gutenberg_viewport_meta_tag', 0 ); + remove_action( 'wp_head', '_wp_render_title_tag', 1 ); + add_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); + + // This file will be included instead of the theme's template file. + return gutenberg_dir_path() . 'lib/template-canvas.php'; +} + +/** + * Displays title tag with content, regardless of whether theme has title-tag support. + * + * @see _wp_render_title_tag() + */ +function gutenberg_render_title_tag() { + echo '' . wp_get_document_title() . '' . "\n"; +} + +/** + * Renders the markup for the current template. + */ +function gutenberg_render_the_template() { + global $_wp_current_template_post; + global $wp_embed; + + if ( ! $_wp_current_template_post || 'wp_template' !== $_wp_current_template_post->post_type ) { + echo '

' . esc_html__( 'No matching template found', 'gutenberg' ) . '

'; + return; + } + + $content = $_wp_current_template_post->post_content; + + $content = $wp_embed->run_shortcode( $content ); + $content = $wp_embed->autoembed( $content ); + $content = do_blocks( $content ); + $content = wptexturize( $content ); + $content = wp_make_content_images_responsive( $content ); + $content = str_replace( ']]>', ']]>', $content ); + + // Wrap block template in .wp-site-blocks to allow for specific descendant styles + // (e.g. `.wp-site-blocks > *`). + echo '
'; + echo $content; // phpcs:ignore WordPress.Security.EscapeOutput + echo '
'; +} + +/** + * Renders a 'viewport' meta tag. + * + * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas. + */ +function gutenberg_viewport_meta_tag() { + echo '' . "\n"; +} + +/** + * Strips .php suffix from template file names. + * + * @access private + * + * @param string $template_file Template file name. + * @return string Template file name without extension. + */ +function gutenberg_strip_php_suffix( $template_file ) { + return preg_replace( '/\.php$/', '', $template_file ); +} diff --git a/lib/templates.php b/lib/templates.php index dce2905b0ae52d..3eb4df27e1c0ec 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -9,28 +9,47 @@ * Registers block editor 'wp_template' post type. */ function gutenberg_register_template_post_type() { - if ( - get_option( 'gutenberg-experiments' ) && - ! array_key_exists( 'gutenberg-full-site-editing', get_option( 'gutenberg-experiments' ) ) - ) { + if ( ! gutenberg_is_experiment_enabled( 'gutenberg-full-site-editing' ) ) { return; } $labels = array( - 'name' => __( 'Templates', 'gutenberg' ), + 'name' => __( 'Templates', 'gutenberg' ), + 'singular_name' => __( 'Template', 'gutenberg' ), + 'menu_name' => _x( 'Templates', 'Admin Menu text', 'gutenberg' ), + 'add_new' => _x( 'Add New', 'Template', 'gutenberg' ), + 'add_new_item' => __( 'Add New Template', 'gutenberg' ), + 'new_item' => __( 'New Template', 'gutenberg' ), + 'edit_item' => __( 'Edit Template', 'gutenberg' ), + 'view_item' => __( 'View Template', 'gutenberg' ), + 'all_items' => __( 'All Templates', 'gutenberg' ), + 'search_items' => __( 'Search Templates', 'gutenberg' ), + 'parent_item_colon' => __( 'Parent Template:', 'gutenberg' ), + 'not_found' => __( 'No templates found.', 'gutenberg' ), + 'not_found_in_trash' => __( 'No templates found in Trash.', 'gutenberg' ), + 'archives' => __( 'Template archives', 'gutenberg' ), + 'insert_into_item' => __( 'Insert into template', 'gutenberg' ), + 'uploaded_to_this_item' => __( 'Uploaded to this template', 'gutenberg' ), + 'filter_items_list' => __( 'Filter templates list', 'gutenberg' ), + 'items_list_navigation' => __( 'Templates list navigation', 'gutenberg' ), + 'items_list' => __( 'Templates list', 'gutenberg' ), ); $args = array( - 'labels' => $labels, - 'description' => __( 'Templates to include in your theme.', 'gutenberg' ), - 'public' => false, - 'has_archive' => false, - 'show_in_rest' => true, - 'rest_base' => 'templates', - 'capability_type' => array( 'template', 'templates' ), - 'map_meta_cap' => true, - 'supports' => array( + 'labels' => $labels, + 'description' => __( 'Templates to include in your theme.', 'gutenberg' ), + 'public' => false, + 'has_archive' => false, + 'show_ui' => true, + 'show_in_menu' => 'themes.php', + 'show_in_admin_bar' => false, + 'show_in_rest' => true, + 'rest_base' => 'templates', + 'capability_type' => array( 'template', 'templates' ), + 'map_meta_cap' => true, + 'supports' => array( 'title', + 'slug', 'editor', 'revisions', ), @@ -65,3 +84,83 @@ function gutenberg_grant_template_caps( array $allcaps ) { return $allcaps; } add_filter( 'user_has_cap', 'gutenberg_grant_template_caps' ); + +/** + * Filters capabilities to prevent deletion of the 'wp_template' post with slug 'index'. + * + * Similar to today's themes, this template should always exist. + * + * @param array $caps Array of the user's capabilities. + * @param string $cap Capability name. + * @param int $user_id The user ID. + * @param array $args Adds the context to the cap. Typically the object ID. + * @return array Filtered $caps. + */ +function gutenberg_prevent_index_template_deletion( $caps, $cap, $user_id, $args ) { + if ( 'delete_post' !== $cap || ! isset( $args[0] ) ) { + return $caps; + } + + $post = get_post( $args[0] ); + if ( ! $post || 'wp_template' !== $post->post_type ) { + return $caps; + } + + if ( 'index' === $post->post_name ) { + $caps[] = 'do_not_allow'; + } + + return $caps; +} +add_filter( 'map_meta_cap', 'gutenberg_prevent_index_template_deletion', 10, 4 ); + +/** + * Fixes the label of the 'wp_template' admin menu entry. + */ +function gutenberg_fix_template_admin_menu_entry() { + global $submenu; + if ( ! isset( $submenu['themes.php'] ) ) { + return; + } + $post_type = get_post_type_object( 'wp_template' ); + if ( ! $post_type ) { + return; + } + foreach ( $submenu['themes.php'] as $key => $submenu_entry ) { + if ( $post_type->labels->all_items === $submenu['themes.php'][ $key ][0] ) { + $submenu['themes.php'][ $key ][0] = $post_type->labels->menu_name; // phpcs:ignore WordPress.WP.GlobalVariablesOverride + break; + } + } +} +add_action( 'admin_menu', 'gutenberg_fix_template_admin_menu_entry' ); + +/** + * Filters the 'wp_template' post type columns in the admin list table. + * + * @param array $columns Columns to display. + * @return array Filtered $columns. + */ +function gutenberg_filter_template_list_table_columns( array $columns ) { + $columns['slug'] = __( 'Slug', 'gutenberg' ); + if ( isset( $columns['date'] ) ) { + unset( $columns['date'] ); + } + return $columns; +} +add_filter( 'manage_wp_template_posts_columns', 'gutenberg_filter_template_list_table_columns' ); + +/** + * Renders column content for the 'wp_template' post type list table. + * + * @param string $column_name Column name to render. + * @param int $post_id Post ID. + */ +function gutenberg_render_template_list_table_column( $column_name, $post_id ) { + if ( 'slug' !== $column_name ) { + return; + } + $post = get_post( $post_id ); + echo esc_html( $post->post_name ); +} +add_action( 'manage_wp_template_posts_custom_column', 'gutenberg_render_template_list_table_column', 10, 2 ); diff --git a/package-lock.json b/package-lock.json index eca7bdfbb2a7ef..e5e1f5810d7b10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "6.6.0", + "version": "6.8.0-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -383,12 +383,12 @@ } }, "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -430,32 +430,32 @@ } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", "dev": true }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -986,15 +986,6 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.4.tgz", - "integrity": "sha512-Ki+Y9nXBlKfhD+LXaRS7v95TtTGYRAf9Y1rTDiE75zf8YQz4GDaWRXosMfJBXxnk88mGFjWdCRIeqDbon7spYA==", - "dev": true, - "requires": { - "regexp-tree": "^0.1.0" - } - }, "@babel/plugin-transform-new-target": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", @@ -1044,9 +1035,9 @@ } }, "@babel/plugin-transform-react-constant-elements": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.6.0.tgz", - "integrity": "sha512-np/nPuII8DHOZWB3u8u+NSeKlEz0eBrOlnVksIQog4C9NGVzXO+NLxMcXn4Eu4GMFzOw2W6Tyo6L3+Wv8z9Y5w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.6.3.tgz", + "integrity": "sha512-1/YogSSU7Tby9rq2VCmhuRg+6pxsHy2rI7w/oo8RKoBt6uBUFG+mk6x13kK+FY1/ggN92HAfg7ADd1v1+NCOKg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", @@ -1325,9 +1316,9 @@ } }, "@babel/preset-react": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", - "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.6.3.tgz", + "integrity": "sha512-07yQhmkZmRAfwREYIQgW0HEwMY9GBJVuPY4Q12UC72AbfaawuupVWa8zQs2tlL+yun45Nv/1KreII/0PLfEsgA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -1357,12 +1348,12 @@ } }, "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -1404,15 +1395,15 @@ } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", "dev": true }, "@babel/plugin-transform-typescript": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.6.0.tgz", - "integrity": "sha512-yzw7EopOOr6saONZ3KA3lpizKnWRTe+rfBqg4AmQbSow7ik7fqmzrfIqt053osLwLE2AaTqGinLM2tl6+M/uog==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.6.3.tgz", + "integrity": "sha512-aiWINBrPMSC3xTXRNM/dfmyYuPNKY/aexYqBgh0HBI5Y+WO5oRAqW/oROYeYHrF4Zw12r9rK4fMk/ZlAmqx/FQ==", "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.6.0", @@ -1421,26 +1412,26 @@ } }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1895,9 +1886,9 @@ } }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "ssri": { @@ -1936,25 +1927,27 @@ } }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } }, "@evocateur/pacote": { - "version": "9.6.3", - "resolved": "https://registry.npmjs.org/@evocateur/pacote/-/pacote-9.6.3.tgz", - "integrity": "sha512-ExqNqcbdHQprEgKnY/uQz7WRtyHRbQxRl4JnVkSkmtF8qffRrF9K+piZKNLNSkRMOT/3H0e3IP44QVCHaXMWOQ==", + "version": "9.6.5", + "resolved": "https://registry.npmjs.org/@evocateur/pacote/-/pacote-9.6.5.tgz", + "integrity": "sha512-EI552lf0aG2nOV8NnZpTxNo2PcXKPmDbF9K8eCBFQdIZwHNGN/mi815fxtmUMa2wTa1yndotICIDt/V0vpEx2w==", "dev": true, "requires": { "@evocateur/npm-registry-fetch": "^4.0.0", "bluebird": "^3.5.3", - "cacache": "^12.0.0", + "cacache": "^12.0.3", + "chownr": "^1.1.2", "figgy-pudding": "^3.5.1", "get-stream": "^4.1.0", "glob": "^7.1.4", + "infer-owner": "^1.0.4", "lru-cache": "^5.1.1", "make-fetch-happen": "^5.0.0", "minimatch": "^3.0.4", @@ -1964,7 +1957,7 @@ "normalize-package-data": "^2.5.0", "npm-package-arg": "^6.1.0", "npm-packlist": "^1.4.4", - "npm-pick-manifest": "^2.2.3", + "npm-pick-manifest": "^3.0.0", "osenv": "^0.1.5", "promise-inflight": "^1.0.1", "promise-retry": "^1.1.1", @@ -1979,15 +1972,15 @@ }, "dependencies": { "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", "dev": true }, "cacache": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.2.tgz", - "integrity": "sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -2008,9 +2001,9 @@ } }, "chownr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "get-stream": { @@ -2023,9 +2016,9 @@ } }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2037,9 +2030,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "lru-cache": { @@ -2107,9 +2100,9 @@ } }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -2122,9 +2115,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "ssri": { @@ -2137,18 +2130,30 @@ } }, "tar": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", - "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.5", + "minipass": "^2.8.6", "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", "yallist": "^3.0.3" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } } }, "unique-filename": { @@ -2167,9 +2172,9 @@ "dev": true }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } @@ -2227,6 +2232,12 @@ "integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==", "dev": true }, + "@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "dev": true + }, "@jest/console": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", @@ -2482,15 +2493,15 @@ } }, "@lerna/add": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.16.2.tgz", - "integrity": "sha512-RAAaF8aODPogj2Ge9Wj3uxPFIBGpog9M+HwSuq03ZnkkO831AmasCTJDqV+GEpl1U2DvnhZQEwHpWmTT0uUeEw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.18.0.tgz", + "integrity": "sha512-Z5EaQbBnJn1LEPb0zb0Q2o9T8F8zOnlCsj6JYpY6aSke17UUT7xx0QMN98iBK+ueUHKjN/vdFdYlNCYRSIdujA==", "dev": true, "requires": { "@evocateur/pacote": "^9.6.3", - "@lerna/bootstrap": "3.16.2", - "@lerna/command": "3.16.0", - "@lerna/filter-options": "3.16.0", + "@lerna/bootstrap": "3.18.0", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.0", "@lerna/npm-conf": "3.16.0", "@lerna/validation-error": "3.13.0", "dedent": "^0.7.0", @@ -2513,34 +2524,23 @@ } } }, - "@lerna/batch-packages": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/batch-packages/-/batch-packages-3.16.0.tgz", - "integrity": "sha512-7AdMkANpubY/FKFI01im01tlx6ygOBJ/0JcixMUWoWP/7Ds3SWQF22ID6fbBr38jUWptYLDs2fagtTDL7YUPuA==", - "dev": true, - "requires": { - "@lerna/package-graph": "3.16.0", - "npmlog": "^4.1.2" - } - }, "@lerna/bootstrap": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.16.2.tgz", - "integrity": "sha512-I+gs7eh6rv9Vyd+CwqL7sftRfOOsSzCle8cv/CGlMN7/p7EAVhxEdAw8SYoHIKHzipXszuqqy1Y3opyleD0qdA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.18.0.tgz", + "integrity": "sha512-3DZKWIaKvr7sUImoKqSz6eqn84SsOVMnA5QHwgzXiQjoeZ/5cg9x2r+Xj3+3w/lvLoh0j8U2GNtrIaPNis4bKQ==", "dev": true, "requires": { - "@lerna/batch-packages": "3.16.0", - "@lerna/command": "3.16.0", - "@lerna/filter-options": "3.16.0", - "@lerna/has-npm-version": "3.16.0", - "@lerna/npm-install": "3.16.0", - "@lerna/package-graph": "3.16.0", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.0", + "@lerna/has-npm-version": "3.16.5", + "@lerna/npm-install": "3.16.5", + "@lerna/package-graph": "3.18.0", "@lerna/pulse-till-done": "3.13.0", - "@lerna/rimraf-dir": "3.14.2", + "@lerna/rimraf-dir": "3.16.5", "@lerna/run-lifecycle": "3.16.2", - "@lerna/run-parallel-batches": "3.16.0", - "@lerna/symlink-binary": "3.16.2", - "@lerna/symlink-dependencies": "3.16.2", + "@lerna/run-topologically": "3.18.0", + "@lerna/symlink-binary": "3.17.0", + "@lerna/symlink-dependencies": "3.17.0", "@lerna/validation-error": "3.13.0", "dedent": "^0.7.0", "get-port": "^4.2.0", @@ -2576,33 +2576,33 @@ } }, "@lerna/changed": { - "version": "3.16.4", - "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.16.4.tgz", - "integrity": "sha512-NCD7XkK744T23iW0wqKEgF4R9MYmReUbyHCZKopFnsNpQdqumc3SOIvQUAkKCP6hQJmYvxvOieoVgy/CVDpZ5g==", + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.18.2.tgz", + "integrity": "sha512-xVnFuj4A6Avxem+8R+KOuKDxfnxp1S5tM5nwsh7n3IhCN5Ga7YINV/JgPhrwgcpqPCVBvAowkilghT/I0r6wUw==", "dev": true, "requires": { - "@lerna/collect-updates": "3.16.0", - "@lerna/command": "3.16.0", - "@lerna/listable": "3.16.0", + "@lerna/collect-updates": "3.18.0", + "@lerna/command": "3.18.0", + "@lerna/listable": "3.18.0", "@lerna/output": "3.13.0", - "@lerna/version": "3.16.4" + "@lerna/version": "3.18.2" } }, "@lerna/check-working-tree": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.14.2.tgz", - "integrity": "sha512-7safqxM/MYoAoxZxulUDtIJIbnBIgo0PB/FHytueG+9VaX7GMnDte2Bt1EKa0dz2sAyQdmQ3Q8ZXpf/6JDjaeg==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.16.5.tgz", + "integrity": "sha512-xWjVBcuhvB8+UmCSb5tKVLB5OuzSpw96WEhS2uz6hkWVa/Euh1A0/HJwn2cemyK47wUrCQXtczBUiqnq9yX5VQ==", "dev": true, "requires": { - "@lerna/collect-uncommitted": "3.14.2", - "@lerna/describe-ref": "3.14.2", + "@lerna/collect-uncommitted": "3.16.5", + "@lerna/describe-ref": "3.16.5", "@lerna/validation-error": "3.13.0" } }, "@lerna/child-process": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-3.14.2.tgz", - "integrity": "sha512-xnq+W5yQb6RkwI0p16ZQnrn6HkloH/MWTw4lGE1nKsBLAUbmSU5oTE93W1nrG0X3IMF/xWc9UYvNdUGMWvZZ4w==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-3.16.5.tgz", + "integrity": "sha512-vdcI7mzei9ERRV4oO8Y1LHBZ3A5+ampRKg1wq5nutLsUA4mEBN6H7JqjWOMY9xZemv6+kATm2ofjJ3lW5TszQg==", "dev": true, "requires": { "chalk": "^2.3.1", @@ -2658,24 +2658,24 @@ } }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } }, "@lerna/clean": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.16.0.tgz", - "integrity": "sha512-5P9U5Y19WmYZr7UAMGXBpY7xCRdlR7zhHy8MAPDKVx70rFIBS6nWXn5n7Kntv74g7Lm1gJ2rsiH5tj1OPcRJgg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.18.0.tgz", + "integrity": "sha512-BiwBELZNkarRQqj+v5NPB1aIzsOX+Y5jkZ9a5UbwHzEdBUQ5lQa0qaMLSOve/fSkaiZQxe6qnTyatN75lOcDMg==", "dev": true, "requires": { - "@lerna/command": "3.16.0", - "@lerna/filter-options": "3.16.0", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.0", "@lerna/prompt": "3.13.0", "@lerna/pulse-till-done": "3.13.0", - "@lerna/rimraf-dir": "3.14.2", + "@lerna/rimraf-dir": "3.16.5", "p-map": "^2.1.0", "p-map-series": "^1.0.0", "p-waterfall": "^1.0.0" @@ -2690,23 +2690,214 @@ } }, "@lerna/cli": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.13.0.tgz", - "integrity": "sha512-HgFGlyCZbYaYrjOr3w/EsY18PdvtsTmDfpUQe8HwDjXlPeCCUgliZjXLOVBxSjiOvPeOSwvopwIHKWQmYbwywg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.18.0.tgz", + "integrity": "sha512-AwDyfGx7fxJgeaZllEuyJ9LZ6Tdv9yqRD9RX762yCJu+PCAFvB9bp6OYuRSGli7QQgM0CuOYnSg4xVNOmuGKDA==", "dev": true, "requires": { "@lerna/global-options": "3.13.0", "dedent": "^0.7.0", "npmlog": "^4.1.2", - "yargs": "^12.0.1" + "yargs": "^14.2.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.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-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "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.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz", + "integrity": "sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.0" + } + }, + "yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@lerna/collect-uncommitted": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-3.16.5.tgz", + "integrity": "sha512-ZgqnGwpDZiWyzIQVZtQaj9tRizsL4dUOhuOStWgTAw1EMe47cvAY2kL709DzxFhjr6JpJSjXV5rZEAeU3VE0Hg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "chalk": "^2.3.1", + "figgy-pudding": "^3.5.1", + "npmlog": "^4.1.2" + } + }, + "@lerna/collect-updates": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.18.0.tgz", + "integrity": "sha512-LJMKgWsE/var1RSvpKDIxS8eJ7POADEc0HM3FQiTpEczhP6aZfv9x3wlDjaHpZm9MxJyQilqxZcasRANmRcNgw==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/describe-ref": "3.16.5", + "minimatch": "^3.0.4", + "npmlog": "^4.1.2", + "slash": "^2.0.0" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "@lerna/command": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.18.0.tgz", + "integrity": "sha512-JQ0TGzuZc9Ky8xtwtSLywuvmkU8X62NTUT3rMNrUykIkOxBaO+tE0O98u2yo/9BYOeTRji9IsjKZEl5i9Qt0xQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/package-graph": "3.18.0", + "@lerna/project": "3.18.0", + "@lerna/validation-error": "3.13.0", + "@lerna/write-log-file": "3.13.0", + "dedent": "^0.7.0", + "execa": "^1.0.0", + "is-ci": "^2.0.0", + "lodash": "^4.17.14", + "npmlog": "^4.1.2" + }, + "dependencies": { "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2735,15 +2926,6 @@ "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", @@ -2753,89 +2935,6 @@ "pump": "^3.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 - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.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.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "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.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "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.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -2847,160 +2946,9 @@ } }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, - "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": { - "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" - } - }, - "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": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "@lerna/collect-uncommitted": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-3.14.2.tgz", - "integrity": "sha512-4EkQu4jIOdNL2BMzy/N0ydHB8+Z6syu6xiiKXOoFl0WoWU9H1jEJCX4TH7CmVxXL1+jcs8FIS2pfQz4oew99Eg==", - "dev": true, - "requires": { - "@lerna/child-process": "3.14.2", - "chalk": "^2.3.1", - "figgy-pudding": "^3.5.1", - "npmlog": "^4.1.2" - } - }, - "@lerna/collect-updates": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.16.0.tgz", - "integrity": "sha512-HwAIl815X2TNlmcp28zCrSdXfoZWNP7GJPEqNWYk7xDJTYLqQ+SrmKUePjb3AMGBwYAraZSEJLbHdBpJ5+cHmQ==", - "dev": true, - "requires": { - "@lerna/child-process": "3.14.2", - "@lerna/describe-ref": "3.14.2", - "minimatch": "^3.0.4", - "npmlog": "^4.1.2", - "slash": "^2.0.0" - }, - "dependencies": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } - } - }, - "@lerna/command": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.16.0.tgz", - "integrity": "sha512-u7tE4GC4/gfbPA9eQg+0ulnoJ+PMoMqomx033r/IxqZrHtmJR9+pF/37S0fsxJ2hX/RMFPC7c9Q/i8NEufSpdQ==", - "dev": true, - "requires": { - "@lerna/child-process": "3.14.2", - "@lerna/package-graph": "3.16.0", - "@lerna/project": "3.16.0", - "@lerna/validation-error": "3.13.0", - "@lerna/write-log-file": "3.13.0", - "dedent": "^0.7.0", - "execa": "^1.0.0", - "is-ci": "^2.0.0", - "lodash": "^4.17.14", - "npmlog": "^4.1.2" - }, - "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 - }, - "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" - } - }, - "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" - } - }, - "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" - } - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -3045,9 +2993,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "pify": { @@ -3075,14 +3023,14 @@ } }, "@lerna/create": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.16.0.tgz", - "integrity": "sha512-OZApR1Iz7awutbmj4sAArwhqCyKgcrnw9rH0aWAUrkYWrD1w4TwkvAcYAsfx5GpQGbLQwoXhoyyPwPfZRRWz3Q==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.18.0.tgz", + "integrity": "sha512-y9oS7ND5T13c+cCTJHa2Y9in02ppzyjsNynVWFuS40eIzZ3z058d9+3qSBt1nkbbQlVyfLoP6+bZPsjyzap5ig==", "dev": true, "requires": { "@evocateur/pacote": "^9.6.3", - "@lerna/child-process": "3.14.2", - "@lerna/command": "3.16.0", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", "@lerna/npm-conf": "3.16.0", "@lerna/validation-error": "3.13.0", "camelcase": "^5.0.0", @@ -3118,9 +3066,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "pify": { @@ -3142,9 +3090,9 @@ "dev": true }, "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -3177,45 +3125,45 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true } } }, "@lerna/describe-ref": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.14.2.tgz", - "integrity": "sha512-qa5pzDRK2oBQXNjyRmRnN7E8a78NMYfQjjlRFB0KNHMsT6mCiL9+8kIS39sSE2NqT8p7xVNo2r2KAS8R/m3CoQ==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.16.5.tgz", + "integrity": "sha512-c01+4gUF0saOOtDBzbLMFOTJDHTKbDFNErEY6q6i9QaXuzy9LNN62z+Hw4acAAZuJQhrVWncVathcmkkjvSVGw==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", + "@lerna/child-process": "3.16.5", "npmlog": "^4.1.2" } }, "@lerna/diff": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.16.0.tgz", - "integrity": "sha512-QUpVs5TPl8vBIne10/vyjUxanQBQQp7Lk3iaB8MnCysKr0O+oy7trWeFVDPEkBTCD177By7yPGyW5Yey1nCBbA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.18.0.tgz", + "integrity": "sha512-3iLNlpurc2nV9k22w8ini2Zjm2UPo3xtQgWyqdA6eJjvge0+5AlNAWfPoV6cV+Hc1xDbJD2YDSFpZPJ1ZGilRw==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", - "@lerna/command": "3.16.0", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", "@lerna/validation-error": "3.13.0", "npmlog": "^4.1.2" } }, "@lerna/exec": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.16.0.tgz", - "integrity": "sha512-mH3O5NXf/O88jBaBBTUf+d56CUkxpg782s3Jxy7HWbVuSUULt3iMRPTh+zEXO5/555etsIVVDDyUR76meklrJA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.18.0.tgz", + "integrity": "sha512-hwkuzg1+38+pbzdZPhGtLIYJ59z498/BCNzR8d4/nfMYm8lFbw9RgJJajLcdbuJ9LJ08cZ93hf8OlzetL84TYg==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", - "@lerna/command": "3.16.0", - "@lerna/filter-options": "3.16.0", - "@lerna/run-topologically": "3.16.0", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.0", + "@lerna/run-topologically": "3.18.0", "@lerna/validation-error": "3.13.0", "p-map": "^2.1.0" }, @@ -3229,20 +3177,22 @@ } }, "@lerna/filter-options": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.16.0.tgz", - "integrity": "sha512-InIi1fF8+PxpCwir9bIy+pGxrdE6hvN0enIs1eNGCVS1TTE8osNgiZXa838bMQ1yaEccdcnVX6Z03BNKd56kNg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.18.0.tgz", + "integrity": "sha512-UGVcixs3TGzD8XSmFSbwUVVQnAjaZ6Rmt8Vuq2RcR98ULkGB1LiGNMY89XaNBhaaA8vx7yQWiLmJi2AfmD63Qg==", "dev": true, "requires": { - "@lerna/collect-updates": "3.16.0", - "@lerna/filter-packages": "3.16.0", - "dedent": "^0.7.0" + "@lerna/collect-updates": "3.18.0", + "@lerna/filter-packages": "3.18.0", + "dedent": "^0.7.0", + "figgy-pudding": "^3.5.1", + "npmlog": "^4.1.2" } }, "@lerna/filter-packages": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.16.0.tgz", - "integrity": "sha512-eGFzQTx0ogkGDCnbTuXqssryR6ilp8+dcXt6B+aq1MaqL/vOJRZyqMm4TY3CUOUnzZCi9S2WWyMw3PnAJOF+kg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.18.0.tgz", + "integrity": "sha512-6/0pMM04bCHNATIOkouuYmPg6KH3VkPCIgTfQmdkPJTullERyEQfNUKikrefjxo1vHOoCACDpy65JYyKiAbdwQ==", "dev": true, "requires": { "@lerna/validation-error": "3.13.0", @@ -3271,9 +3221,9 @@ }, "dependencies": { "chownr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "fs-extra": { @@ -3288,11 +3238,21 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, "ssri": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", @@ -3303,14 +3263,14 @@ } }, "tar": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", - "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.5", + "minipass": "^2.8.6", "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", @@ -3318,20 +3278,20 @@ } }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } }, "@lerna/github-client": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.16.0.tgz", - "integrity": "sha512-IVJjcKjkYaUEPJsDyAblHGEFFNKCRyMagbIDm14L7Ab94ccN6i4TKOqAFEJn2SJHYvKKBdp3Zj2zNlASOMe3DA==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.16.5.tgz", + "integrity": "sha512-rHQdn8Dv/CJrO3VouOP66zAcJzrHsm+wFuZ4uGAai2At2NkgKH+tpNhQy2H1PSC0Ezj9LxvdaHYrUzULqVK5Hw==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", + "@lerna/child-process": "3.16.5", "@octokit/plugin-enterprise-rest": "^3.6.1", "@octokit/rest": "^16.28.4", "git-url-parse": "^11.1.2", @@ -3339,9 +3299,9 @@ }, "dependencies": { "@octokit/request": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.0.2.tgz", - "integrity": "sha512-z1BQr43g4kOL4ZrIVBMHwi68Yg9VbkRUyuAgqCp1rU3vbYa69+2gIld/+gHclw15bJWQnhqqyEb7h5a5EqgZ0A==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.2.1.tgz", + "integrity": "sha512-onjQo4QKyiMAqLM6j3eH8vWw1LEfNCpoZUl6a+TrZVJM1wysBC8F0GhK9K/Vc9UsScSmVs2bstOVD34xpQ2wqQ==", "dev": true, "requires": { "@octokit/endpoint": "^5.1.0", @@ -3350,16 +3310,16 @@ "is-plain-object": "^3.0.0", "node-fetch": "^2.3.0", "once": "^1.4.0", - "universal-user-agent": "^3.0.0" + "universal-user-agent": "^4.0.0" } }, "@octokit/rest": { - "version": "16.28.7", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.28.7.tgz", - "integrity": "sha512-cznFSLEhh22XD3XeqJw51OLSfyL2fcFKUO+v2Ep9MTAFfFLS1cK1Zwd1yEgQJmJoDnj4/vv3+fGGZweG+xsbIA==", + "version": "16.33.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.33.1.tgz", + "integrity": "sha512-lOQ+fJZwkeJ/1PRTdnY1uNja01aKOMioRhQfZtei64gZMXIX3EAfF4koMQMvoLFwsnVBu3ifj1JW1WAAKdXcnA==", "dev": true, "requires": { - "@octokit/request": "^5.0.0", + "@octokit/request": "^5.2.0", "@octokit/request-error": "^1.0.2", "atob-lite": "^2.0.0", "before-after-hook": "^2.0.0", @@ -3370,8 +3330,7 @@ "lodash.uniq": "^4.5.0", "octokit-pagination-methods": "^1.1.0", "once": "^1.4.0", - "universal-user-agent": "^3.0.0", - "url-template": "^2.0.8" + "universal-user-agent": "^4.0.0" } }, "before-after-hook": { @@ -3402,12 +3361,12 @@ "dev": true }, "universal-user-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", - "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", "dev": true, "requires": { - "os-name": "^3.0.0" + "os-name": "^3.1.0" } } } @@ -3430,9 +3389,9 @@ "dev": true }, "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -3449,12 +3408,12 @@ "dev": true }, "@lerna/has-npm-version": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.16.0.tgz", - "integrity": "sha512-TIY036dA9J8OyTrZq9J+it2DVKifL65k7hK8HhkUPpitJkw6jwbMObA/8D40LOGgWNPweJWqmlrTbRSwsR7DrQ==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.16.5.tgz", + "integrity": "sha512-WL7LycR9bkftyqbYop5rEGJ9sRFIV55tSGmbN1HLrF9idwOCD7CLrT64t235t3t4O5gehDnwKI5h2U3oxTrF8Q==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", + "@lerna/child-process": "3.16.5", "semver": "^6.2.0" }, "dependencies": { @@ -3467,13 +3426,13 @@ } }, "@lerna/import": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.16.0.tgz", - "integrity": "sha512-trsOmGHzw0rL/f8BLNvd+9PjoTkXq2Dt4/V2UCha254hMQaYutbxcYu8iKPxz9x86jSPlH7FpbTkkHXDsoY7Yg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.18.0.tgz", + "integrity": "sha512-2pYIkkBTZsEdccfc+dPsKZeSw3tBzKSyl0b2lGrfmNX2Y41qqOzsJCyI1WO1uvEIP8aOaLy4hPpqRIBe4ee7hw==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", - "@lerna/command": "3.16.0", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", "@lerna/prompt": "3.13.0", "@lerna/pulse-till-done": "3.13.0", "@lerna/validation-error": "3.13.0", @@ -3494,21 +3453,21 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true } } }, "@lerna/init": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.16.0.tgz", - "integrity": "sha512-Ybol/x5xMtBgokx4j7/Y3u0ZmNh0NiSWzBFVaOs2NOJKvuqrWimF67DKVz7yYtTYEjtaMdug64ohFF4jcT/iag==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.18.0.tgz", + "integrity": "sha512-/vHpmXkMlSaJaq25v5K13mcs/2L7E32O6dSsEkHaZCDRiV2BOqsZng9jjbE/4ynfsWfLLlU9ZcydwG72C3I+mQ==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", - "@lerna/command": "3.16.0", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", "fs-extra": "^8.1.0", "p-map": "^2.1.0", "write-json-file": "^3.2.0" @@ -3526,9 +3485,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "p-map": { @@ -3540,14 +3499,14 @@ } }, "@lerna/link": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.16.2.tgz", - "integrity": "sha512-eCPg5Lo8HT525fIivNoYF3vWghO3UgEVFdbsiPmhzwI7IQyZro5HWYzLtywSAdEog5XZpd2Bbn0CsoHWBB3gww==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.18.0.tgz", + "integrity": "sha512-FbbIpH0EpsC+dpAbvxCoF3cn7F1MAyJjEa5Lh3XkDGATOlinMFuKCbmX0NLpOPQZ5zghvrui97cx+jz5F2IlHw==", "dev": true, "requires": { - "@lerna/command": "3.16.0", - "@lerna/package-graph": "3.16.0", - "@lerna/symlink-dependencies": "3.16.2", + "@lerna/command": "3.18.0", + "@lerna/package-graph": "3.18.0", + "@lerna/symlink-dependencies": "3.17.0", "p-map": "^2.1.0", "slash": "^2.0.0" }, @@ -3567,24 +3526,24 @@ } }, "@lerna/list": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.16.0.tgz", - "integrity": "sha512-TkvstoPsgKqqQ0KfRumpsdMXfRSEhdXqOLq519XyI5IRWYxhoqXqfi8gG37UoBPhBNoe64japn5OjphF3rOmQA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.18.0.tgz", + "integrity": "sha512-mpB7Q6T+n2CaiPFz0LuOE+rXphDfHm0mKIwShnyS/XDcii8jXv+z9Iytj8p3rfCH2I1L80j2qL6jWzyGy/uzKA==", "dev": true, "requires": { - "@lerna/command": "3.16.0", - "@lerna/filter-options": "3.16.0", - "@lerna/listable": "3.16.0", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.0", + "@lerna/listable": "3.18.0", "@lerna/output": "3.13.0" } }, "@lerna/listable": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.16.0.tgz", - "integrity": "sha512-mtdAT2EEECqrJSDm/aXlOUFr1MRE4p6hppzY//Klp05CogQy6uGaKk+iKG5yyCLaOXFFZvG4HfO11CmoGSDWzw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.18.0.tgz", + "integrity": "sha512-9gLGKYNLSKeurD+sJ2RA+nz4Ftulr91U127gefz0RlmAPpYSjwcJkxwa0UfJvpQTXv9C7yzHLnn0BjyAQRjuew==", "dev": true, "requires": { - "@lerna/query-graph": "3.16.0", + "@lerna/query-graph": "3.18.0", "chalk": "^2.3.1", "columnify": "^1.5.4" } @@ -3620,9 +3579,9 @@ } }, "@lerna/npm-dist-tag": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.16.0.tgz", - "integrity": "sha512-MQrBkqJJB9+eNphuj9w90QPMOs4NQXMuSRk9NqzeFunOmdDopPCV0Q7IThSxEuWnhJ2n3B7G0vWUP7tNMPdqIQ==", + "version": "3.18.1", + "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.18.1.tgz", + "integrity": "sha512-vWkZh2T/O9OjPLDrba0BTWO7ug/C3sCwjw7Qyk1aEbxMBXB/eEJPqirwJTWT+EtRJQYB01ky3K8ZFOhElVyjLw==", "dev": true, "requires": { "@evocateur/npm-registry-fetch": "^4.0.0", @@ -3633,12 +3592,12 @@ } }, "@lerna/npm-install": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.16.0.tgz", - "integrity": "sha512-APUOIilZCzDzce92uLEwzt1r7AEMKT/hWA1ThGJL+PO9Rn8A95Km3o2XZAYG4W0hR+P4O2nSVuKbsjQtz8CjFQ==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.16.5.tgz", + "integrity": "sha512-hfiKk8Eku6rB9uApqsalHHTHY+mOrrHeWEs+gtg7+meQZMTS3kzv4oVp5cBZigndQr3knTLjwthT/FX4KvseFg==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", + "@lerna/child-process": "3.16.5", "@lerna/get-npm-exec-opts": "3.13.0", "fs-extra": "^8.1.0", "npm-package-arg": "^6.1.0", @@ -3659,9 +3618,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true } } @@ -3695,9 +3654,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "pify": { @@ -3709,12 +3668,12 @@ } }, "@lerna/npm-run-script": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.14.2.tgz", - "integrity": "sha512-LbVFv+nvAoRTYLMrJlJ8RiakHXrLslL7Jp/m1R18vYrB8LYWA3ey+nz5Tel2OELzmjUiemAKZsD9h6i+Re5egg==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.16.5.tgz", + "integrity": "sha512-1asRi+LjmVn3pMjEdpqKJZFT/3ZNpb+VVeJMwrJaV/3DivdNg7XlPK9LTrORuKU4PSvhdEZvJmSlxCKyDpiXsQ==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", + "@lerna/child-process": "3.16.5", "@lerna/get-npm-exec-opts": "3.13.0", "npmlog": "^4.1.2" } @@ -3755,20 +3714,30 @@ }, "dependencies": { "chownr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, "tar": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", - "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.5", + "minipass": "^2.8.6", "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", @@ -3776,9 +3745,9 @@ } }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } @@ -3795,9 +3764,9 @@ }, "dependencies": { "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "load-json-file": { @@ -3838,9 +3807,9 @@ } }, "@lerna/package-graph": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.16.0.tgz", - "integrity": "sha512-A2mum/gNbv7zCtAwJqoxzqv89As73OQNK2MgSX1SHWya46qoxO9a9Z2c5lOFQ8UFN5ZxqWMfFYXRCz7qzwmFXw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.18.0.tgz", + "integrity": "sha512-BLYDHO5ihPh20i3zoXfLZ5ZWDCrPuGANgVhl7k5pCmRj90LCvT+C7V3zrw70fErGAfvkcYepMqxD+oBrAYwquQ==", "dev": true, "requires": { "@lerna/prerelease-id-from-version": "3.16.0", @@ -3876,9 +3845,9 @@ } }, "@lerna/project": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.16.0.tgz", - "integrity": "sha512-NrKcKK1EqXqhrGvslz6Q36+ZHuK3zlDhGdghRqnxDcHxMPT01NgLcmsnymmQ+gjMljuLRmvKYYCuHrknzX8VrA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.18.0.tgz", + "integrity": "sha512-+LDwvdAp0BurOAWmeHE3uuticsq9hNxBI0+FMHiIai8jrygpJGahaQrBYWpwbshbQyVLeQgx3+YJdW2TbEdFWA==", "dev": true, "requires": { "@lerna/package": "3.16.0", @@ -3908,18 +3877,18 @@ } }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "import-fresh": { @@ -4009,22 +3978,22 @@ } }, "@lerna/publish": { - "version": "3.16.4", - "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.16.4.tgz", - "integrity": "sha512-XZY+gRuF7/v6PDQwl7lvZaGWs8CnX6WIPIu+OCcyFPSL/rdWegdN7HieKBHskgX798qRQc2GrveaY7bNoTKXAw==", + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.18.2.tgz", + "integrity": "sha512-lLQOjoaFv/gc9HtOCMRsOK6NIub8eHnnvmQARjXY/HayA8GuLaD2Px9xOu1L7il+Q0LlMU3wASB9Khy/CiHJUQ==", "dev": true, "requires": { "@evocateur/libnpmaccess": "^3.1.2", "@evocateur/npm-registry-fetch": "^4.0.0", "@evocateur/pacote": "^9.6.3", - "@lerna/check-working-tree": "3.14.2", - "@lerna/child-process": "3.14.2", - "@lerna/collect-updates": "3.16.0", - "@lerna/command": "3.16.0", - "@lerna/describe-ref": "3.14.2", + "@lerna/check-working-tree": "3.16.5", + "@lerna/child-process": "3.16.5", + "@lerna/collect-updates": "3.18.0", + "@lerna/command": "3.18.0", + "@lerna/describe-ref": "3.16.5", "@lerna/log-packed": "3.16.0", "@lerna/npm-conf": "3.16.0", - "@lerna/npm-dist-tag": "3.16.0", + "@lerna/npm-dist-tag": "3.18.1", "@lerna/npm-publish": "3.16.2", "@lerna/otplease": "3.16.0", "@lerna/output": "3.13.0", @@ -4033,9 +4002,9 @@ "@lerna/prompt": "3.13.0", "@lerna/pulse-till-done": "3.13.0", "@lerna/run-lifecycle": "3.16.2", - "@lerna/run-topologically": "3.16.0", + "@lerna/run-topologically": "3.18.0", "@lerna/validation-error": "3.13.0", - "@lerna/version": "3.16.4", + "@lerna/version": "3.18.2", "figgy-pudding": "^3.5.1", "fs-extra": "^8.1.0", "npm-package-arg": "^6.1.0", @@ -4058,9 +4027,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "p-map": { @@ -4087,12 +4056,12 @@ } }, "@lerna/query-graph": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-3.16.0.tgz", - "integrity": "sha512-p0RO+xmHDO95ChJdWkcy9TNLysLkoDARXeRHzY5U54VCwl3Ot/2q8fMCVlA5UeGXDutEyyByl3URqEpcQCWI7Q==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-3.18.0.tgz", + "integrity": "sha512-fgUhLx6V0jDuKZaKj562jkuuhrfVcjl5sscdfttJ8dXNVADfDz76nzzwLY0ZU7/0m69jDedohn5Fx5p7hDEVEg==", "dev": true, "requires": { - "@lerna/package-graph": "3.16.0", + "@lerna/package-graph": "3.18.0", "figgy-pudding": "^3.5.1" } }, @@ -4119,36 +4088,36 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true } } }, "@lerna/rimraf-dir": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.14.2.tgz", - "integrity": "sha512-eFNkZsy44Bu9v1Hrj5Zk6omzg8O9h/7W6QYK1TTUHeyrjTEwytaNQlqF0lrTLmEvq55sviV42NC/8P3M2cvq8Q==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.16.5.tgz", + "integrity": "sha512-bQlKmO0pXUsXoF8lOLknhyQjOZsCc0bosQDoX4lujBXSWxHVTg1VxURtWf2lUjz/ACsJVDfvHZbDm8kyBk5okA==", "dev": true, "requires": { - "@lerna/child-process": "3.14.2", + "@lerna/child-process": "3.16.5", "npmlog": "^4.1.2", "path-exists": "^3.0.0", "rimraf": "^2.6.2" } }, "@lerna/run": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.16.0.tgz", - "integrity": "sha512-woTeLlB1OAAz4zzjdI6RyIxSGuxiUPHJZm89E1pDEPoWwtQV6HMdMgrsQd9ATsJ5Ez280HH4bF/LStAlqW8Ufg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.18.0.tgz", + "integrity": "sha512-sblxHBZ9djaaG7wefPcfEicDqzrB7CP1m/jIB0JvPEQwG4C2qp++ewBpkjRw/mBtjtzg0t7v0nNMXzaWYrQckQ==", "dev": true, "requires": { - "@lerna/command": "3.16.0", - "@lerna/filter-options": "3.16.0", - "@lerna/npm-run-script": "3.14.2", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.0", + "@lerna/npm-run-script": "3.16.5", "@lerna/output": "3.13.0", - "@lerna/run-topologically": "3.16.0", + "@lerna/run-topologically": "3.18.0", "@lerna/timer": "3.13.0", "@lerna/validation-error": "3.13.0", "p-map": "^2.1.0" @@ -4174,39 +4143,21 @@ "npmlog": "^4.1.2" } }, - "@lerna/run-parallel-batches": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/run-parallel-batches/-/run-parallel-batches-3.16.0.tgz", - "integrity": "sha512-2J/Nyv+MvogmQEfC7VcS21ifk7w0HVvzo2yOZRPvkCzGRu/rducxtB4RTcr58XCZ8h/Bt1aqQYKExu3c/3GXwg==", - "dev": true, - "requires": { - "p-map": "^2.1.0", - "p-map-series": "^1.0.0" - }, - "dependencies": { - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - } - } - }, "@lerna/run-topologically": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-3.16.0.tgz", - "integrity": "sha512-4Hlpv4zDtKWa5Z0tPkeu0sK+bxZEKgkNESMGmWrUCNfj7xwvAJurcraK8+a2Y0TFYwf0qjSLY/MzX+ZbJA3Cgw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-3.18.0.tgz", + "integrity": "sha512-lrfEewwuUMC3ioxf9Z9NdHUakN6ihekcPfdYbzR2slmdbjYKmIA5srkWdrK8NwOpQCAuekpOovH2s8X3FGEopg==", "dev": true, "requires": { - "@lerna/query-graph": "3.16.0", + "@lerna/query-graph": "3.18.0", "figgy-pudding": "^3.5.1", "p-queue": "^4.0.0" } }, "@lerna/symlink-binary": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.16.2.tgz", - "integrity": "sha512-kz9XVoFOGSF83gg4gBqH+mG6uxfJfTp8Uy+Cam40CvMiuzfODrGkjuBEFoM/uO2QOAwZvbQDYOBpKUa9ZxHS1Q==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.17.0.tgz", + "integrity": "sha512-RLpy9UY6+3nT5J+5jkM5MZyMmjNHxZIZvXLV+Q3MXrf7Eaa1hNqyynyj4RO95fxbS+EZc4XVSk25DGFQbcRNSQ==", "dev": true, "requires": { "@lerna/create-symlink": "3.16.2", @@ -4227,9 +4178,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "p-map": { @@ -4241,14 +4192,14 @@ } }, "@lerna/symlink-dependencies": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.16.2.tgz", - "integrity": "sha512-wnZqGJQ+Jvr1I3inxrkffrFZfmQI7Ta8gySw/UWCy95QtZWF/f5yk8zVIocCAsjzD0wgb3jJE3CFJ9W5iwWk1A==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.17.0.tgz", + "integrity": "sha512-KmjU5YT1bpt6coOmdFueTJ7DFJL4H1w5eF8yAQ2zsGNTtZ+i5SGFBWpb9AQaw168dydc3s4eu0W0Sirda+F59Q==", "dev": true, "requires": { "@lerna/create-symlink": "3.16.2", "@lerna/resolve-symlink": "3.16.0", - "@lerna/symlink-binary": "3.16.2", + "@lerna/symlink-binary": "3.17.0", "fs-extra": "^8.1.0", "p-finally": "^1.0.0", "p-map": "^2.1.0", @@ -4267,9 +4218,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "p-map": { @@ -4296,26 +4247,27 @@ } }, "@lerna/version": { - "version": "3.16.4", - "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.16.4.tgz", - "integrity": "sha512-ikhbMeIn5ljCtWTlHDzO4YvTmpGTX1lWFFIZ79Vd1TNyOr+OUuKLo/+p06mCl2WEdZu0W2s5E9oxfAAQbyDxEg==", + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.18.2.tgz", + "integrity": "sha512-nmCJpw3A2DoNGbjmvI5UB3ZwTQUayI8M/b3rOA6ZzYeGmoQmm2QBKun05aBRasqTuUo4XjSuas5CqHN+x4f8Ww==", "dev": true, "requires": { - "@lerna/check-working-tree": "3.14.2", - "@lerna/child-process": "3.14.2", - "@lerna/collect-updates": "3.16.0", - "@lerna/command": "3.16.0", + "@lerna/check-working-tree": "3.16.5", + "@lerna/child-process": "3.16.5", + "@lerna/collect-updates": "3.18.0", + "@lerna/command": "3.18.0", "@lerna/conventional-commits": "3.16.4", - "@lerna/github-client": "3.16.0", + "@lerna/github-client": "3.16.5", "@lerna/gitlab-client": "3.15.0", "@lerna/output": "3.13.0", "@lerna/prerelease-id-from-version": "3.16.0", "@lerna/prompt": "3.13.0", "@lerna/run-lifecycle": "3.16.2", - "@lerna/run-topologically": "3.16.0", + "@lerna/run-topologically": "3.18.0", "@lerna/validation-error": "3.13.0", "chalk": "^2.3.1", "dedent": "^0.7.0", + "load-json-file": "^5.3.0", "minimatch": "^3.0.4", "npmlog": "^4.1.2", "p-map": "^2.1.0", @@ -4324,15 +4276,51 @@ "p-waterfall": "^1.0.0", "semver": "^6.2.0", "slash": "^2.0.0", - "temp-write": "^3.4.0" + "temp-write": "^3.4.0", + "write-json-file": "^3.2.0" }, "dependencies": { + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + } + }, "p-map": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "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": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -4344,6 +4332,12 @@ "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 } } }, @@ -4454,12 +4448,12 @@ } }, "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -4477,9 +4471,9 @@ } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", "dev": true }, "@babel/template": { @@ -4494,26 +4488,26 @@ } }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -4903,9 +4897,9 @@ } }, "@parcel/logger": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-1.11.0.tgz", - "integrity": "sha512-lIRfDg+junbFUUeU0QtHX00gKCgEsYHZydFKwrJ8dc0D+WE2SYT1FcVCgpPAfKYgtg0QQMns8E9vzT9UjH92PQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-1.11.1.tgz", + "integrity": "sha512-9NF3M6UVeP2udOBDILuoEHd8VrF4vQqoWHEafymO1pfSoOMfxrSJZw1MfyAAIUN/IFp9qjcpDCUbDZB+ioVevA==", "dev": true, "requires": { "@parcel/workers": "^1.11.0", @@ -4944,13 +4938,13 @@ "dev": true }, "@parcel/watcher": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-1.12.0.tgz", - "integrity": "sha512-yijGiAqG7Tjf5WnFwOkiNWwerfZQDNABldiiqRDtr7vDWLO+F/DIncyB7tTcaD5Loevrr5mzzGo8Ntf3d2GIPg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-1.12.1.tgz", + "integrity": "sha512-od+uCtCxC/KoNQAIE1vWx1YTyKYY+7CTrxBJPRh3cDWw/C0tCtlBMVlrbplscGoEpt6B27KhJDCv82PBxOERNA==", "dev": true, "requires": { "@parcel/utils": "^1.11.0", - "chokidar": "^2.0.3" + "chokidar": "^2.1.5" } }, "@parcel/workers": { @@ -5125,17 +5119,17 @@ } }, "@storybook/addon-a11y": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-5.2.3.tgz", - "integrity": "sha512-1N8hes5J3qyhCahHyFUXwiKif3JH892dzGalxc46jtvC0oCMM0r6lu+lj9J6THc15Oc8Icy9KJZuw4R0wRJl0Q==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-5.2.4.tgz", + "integrity": "sha512-m7D78LiREpUzw7U+jiJXQXSsYoYZ+IkewBIxX5K15lh58AqTi0hD8DWHssgbi76GQwdaXKmMPIwI842dgPYobQ==", "dev": true, "requires": { - "@storybook/addons": "5.2.3", - "@storybook/api": "5.2.3", - "@storybook/client-logger": "5.2.3", - "@storybook/components": "5.2.3", - "@storybook/core-events": "5.2.3", - "@storybook/theming": "5.2.3", + "@storybook/addons": "5.2.4", + "@storybook/api": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/components": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/theming": "5.2.4", "axe-core": "^3.3.2", "common-tags": "^1.8.0", "core-js": "^3.0.1", @@ -5177,9 +5171,9 @@ } }, "@storybook/addon-docs": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-5.2.3.tgz", - "integrity": "sha512-xMg/LCUfVX1SNplaPbsNt9L78/QbxkR7pPpQuLjCvTAvzKkLNWNs8SlwM14ds/SfUNQ9EaCoAd1OQIdZrzrBRg==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-5.2.4.tgz", + "integrity": "sha512-rfzoQUseb3G1WanQckjH85OXJJXMigod/YEEqJv68vo7GVY5PdVkOISy9nSldjrnwH6ikT3pcfJKPuHWRKAx/A==", "dev": true, "requires": { "@babel/generator": "^7.4.0", @@ -5188,12 +5182,12 @@ "@mdx-js/loader": "^1.1.0", "@mdx-js/mdx": "^1.1.0", "@mdx-js/react": "^1.0.27", - "@storybook/addons": "5.2.3", - "@storybook/api": "5.2.3", - "@storybook/components": "5.2.3", - "@storybook/router": "5.2.3", - "@storybook/source-loader": "5.2.3", - "@storybook/theming": "5.2.3", + "@storybook/addons": "5.2.4", + "@storybook/api": "5.2.4", + "@storybook/components": "5.2.4", + "@storybook/router": "5.2.4", + "@storybook/source-loader": "5.2.4", + "@storybook/theming": "5.2.4", "core-js": "^3.0.1", "global": "^4.3.2", "js-string-escape": "^1.0.1", @@ -5201,173 +5195,404 @@ "prop-types": "^15.7.2" } }, - "@storybook/addon-storysource": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-5.2.3.tgz", - "integrity": "sha512-7t/oqRzD2QrGiqnUxU++jqgA8OVYByCFzeRHYcpoz84sd2vSy+110Me1OHj2ZHh1t+QOn8D0DJewe0QrycR56w==", + "@storybook/addon-knobs": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-knobs/-/addon-knobs-5.2.4.tgz", + "integrity": "sha512-VYxbDARJs5RwTEOlcfa98tkDXLcRocB7QXLqt8wwCdXPIqkuoVeQLROXGYJm2NzSn49RyHPKUuVWnRhy34qBbQ==", "dev": true, "requires": { - "@storybook/addons": "5.2.3", - "@storybook/components": "5.2.3", - "@storybook/router": "5.2.3", - "@storybook/source-loader": "5.2.3", - "@storybook/theming": "5.2.3", + "@storybook/addons": "5.2.4", + "@storybook/api": "5.2.4", + "@storybook/client-api": "5.2.4", + "@storybook/components": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/theming": "5.2.4", + "@types/react-color": "^3.0.1", + "copy-to-clipboard": "^3.0.8", "core-js": "^3.0.1", - "estraverse": "^4.2.0", - "loader-utils": "^1.2.3", - "prettier": "^1.16.4", + "escape-html": "^1.0.3", + "fast-deep-equal": "^2.0.1", + "global": "^4.3.2", + "lodash": "^4.17.11", "prop-types": "^15.7.2", - "react-syntax-highlighter": "^8.0.1", - "regenerator-runtime": "^0.12.1", - "util-deprecate": "^1.0.2" + "qs": "^6.6.0", + "react-color": "^2.17.0", + "react-lifecycles-compat": "^3.0.4", + "react-select": "^3.0.0" }, "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true + "@storybook/addons": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-5.2.4.tgz", + "integrity": "sha512-Q+bnVlBA308qnELxnh18hBDRSUgltR9KbV537285dUL/okv/NC6n51mxJwIaG+ksBW2wU+5e6tqSayaKF3uHLw==", + "dev": true, + "requires": { + "@storybook/api": "5.2.4", + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/core-events": "5.2.4", + "core-js": "^3.0.1", + "global": "^4.3.2", + "util-deprecate": "^1.0.2" + } }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "@storybook/api": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-5.2.4.tgz", + "integrity": "sha512-KqAB+NkHIHdwu749NDP+7i44jy1bFgpq7GTJlG+sx/XLZHQveK/8yn109g9bXHFth7SvdXI1+9GA/apzwBU/Mw==", "dev": true, "requires": { - "minimist": "^1.2.0" + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/router": "5.2.4", + "@storybook/theming": "5.2.4", + "core-js": "^3.0.1", + "fast-deep-equal": "^2.0.1", + "global": "^4.3.2", + "lodash": "^4.17.11", + "memoizerific": "^1.11.3", + "prop-types": "^15.6.2", + "react": "^16.8.3", + "semver": "^6.0.0", + "shallow-equal": "^1.1.0", + "store2": "^2.7.1", + "telejson": "^3.0.2", + "util-deprecate": "^1.0.2" } }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "@storybook/channel-postmessage": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-5.2.4.tgz", + "integrity": "sha512-ic7/Ho8z2/aOMjoEbr5p8rijOfO3SZdJnwMvDdUxrqvYq7yACZWidPo3w2+iBwQi9HLqEsWesP1c2doJBxVGRw==", "dev": true, "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "core-js": "^3.0.1", + "global": "^4.3.2", + "telejson": "^3.0.2" + } + }, + "@storybook/channels": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-5.2.4.tgz", + "integrity": "sha512-/r39yEZ5QiGdiq95DhXBypdBo7urkD3Sp1WDyK48uGkZ0gdHWSPy3BBy8OJhEhfNz7nVisTiVIBr4gIrubKDjw==", + "dev": true, + "requires": { + "core-js": "^3.0.1" + } + }, + "@storybook/client-api": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-5.2.4.tgz", + "integrity": "sha512-SOwzEFHoNapURhNqdcI7HA76o5tkWvs2+2s++i/S7xsAd3KyefIVDOdqSMlAxJkxZb8Mlrb3UNRxlrpA8SZqNA==", + "dev": true, + "requires": { + "@storybook/addons": "5.2.4", + "@storybook/channel-postmessage": "5.2.4", + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/router": "5.2.4", + "common-tags": "^1.8.0", + "core-js": "^3.0.1", + "eventemitter3": "^4.0.0", + "global": "^4.3.2", + "is-plain-object": "^3.0.0", + "lodash": "^4.17.11", + "memoizerific": "^1.11.3", + "qs": "^6.6.0", + "util-deprecate": "^1.0.2" + } + }, + "@storybook/client-logger": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-5.2.4.tgz", + "integrity": "sha512-ofp6QQPQZBU+RvlAH5KpZRsfAFHecCZDnl/7YG6FwjHseJr3jHTYmBGGjJDMHFHq+Q7FGQu/yVb9lMFgoQ43QQ==", + "dev": true, + "requires": { + "core-js": "^3.0.1" + } + }, + "@storybook/components": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-5.2.4.tgz", + "integrity": "sha512-APhw+XGag0RTCRJ8eCWKVr8dLt9SRqnS8LtzcZJbokCYRxRTFzhmX2eVEE1v+d0gHib1/yh2COxOjMzv3m/rQA==", + "dev": true, + "requires": { + "@storybook/client-logger": "5.2.4", + "@storybook/theming": "5.2.4", + "@types/react-syntax-highlighter": "10.1.0", + "core-js": "^3.0.1", + "global": "^4.3.2", + "markdown-to-jsx": "^6.9.1", + "memoizerific": "^1.11.3", + "polished": "^3.3.1", + "popper.js": "^1.14.7", + "prop-types": "^15.7.2", + "react": "^16.8.3", + "react-dom": "^16.8.3", + "react-focus-lock": "^1.18.3", + "react-helmet-async": "^1.0.2", + "react-popper-tooltip": "^2.8.3", + "react-syntax-highlighter": "^8.0.1", + "react-textarea-autosize": "^7.1.0", + "simplebar-react": "^1.0.0-alpha.6" + } + }, + "@storybook/core-events": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.2.4.tgz", + "integrity": "sha512-nQknCmaz2S2HW6PSGcuFzve7Y1Js2Cb268vUG0ZMNtJZwFawqYc+KSQHqmOY0pVm8dyROTcWCudPA0k+hk6N5Q==", + "dev": true, + "requires": { + "core-js": "^3.0.1" + } + }, + "@storybook/router": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-5.2.4.tgz", + "integrity": "sha512-GL7eGdj5oYST0mE9fThJB9ye9tTTgrP+aP3okZ6MeMGtNytb7bmJRpAD2E4ouuPTQVppyHI5re8g/HUxUNOT1g==", + "dev": true, + "requires": { + "@reach/router": "^1.2.1", + "@types/reach__router": "^1.2.3", + "core-js": "^3.0.1", + "global": "^4.3.2", + "lodash": "^4.17.11", + "memoizerific": "^1.11.3", + "qs": "^6.6.0" + } + }, + "@storybook/theming": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-5.2.4.tgz", + "integrity": "sha512-2ZlqBrmnm8N0352Fnu2+GB3pEsHL4Eb2eKxV0VLLgkjJuAlm7CK6+I/e4ZknQWxwYm2pQj1y6ta68A62fGBYyA==", + "dev": true, + "requires": { + "@emotion/core": "^10.0.14", + "@emotion/styled": "^10.0.14", + "@storybook/client-logger": "5.2.4", + "common-tags": "^1.8.0", + "core-js": "^3.0.1", + "deep-object-diff": "^1.1.0", + "emotion-theming": "^10.0.14", + "global": "^4.3.2", + "memoizerific": "^1.11.3", + "polished": "^3.3.1", + "prop-types": "^15.7.2", + "resolve-from": "^5.0.0" } }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", - "dev": true - } - } - }, - "@storybook/addon-viewport": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-5.2.3.tgz", - "integrity": "sha512-YN0rQ6GrgDTAQv0KVhSJ/ZBQfEUerOTMsAt9MH9fCIgIfXmXc8BoqkL3DYhAhEPEKpzQkV+NwZyZcLCntXPdQg==", - "dev": true, - "requires": { - "@storybook/addons": "5.2.3", - "@storybook/api": "5.2.3", - "@storybook/client-logger": "5.2.3", - "@storybook/components": "5.2.3", - "@storybook/core-events": "5.2.3", - "@storybook/theming": "5.2.3", - "core-js": "^3.0.1", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "prop-types": "^15.7.2", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/addons": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-5.2.3.tgz", - "integrity": "sha512-LTkUJB8ZDc4++yt9acNHNjlnGWCyNtP+NVYPDvg7zFOaMip21Pj4T0pg9UwYxdqrFBWz9tVz7DJeXroS3egXxg==", - "dev": true, - "requires": { - "@storybook/api": "5.2.3", - "@storybook/channels": "5.2.3", - "@storybook/client-logger": "5.2.3", - "@storybook/core-events": "5.2.3", - "core-js": "^3.0.1", - "global": "^4.3.2", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/api": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-5.2.3.tgz", - "integrity": "sha512-2csxa/d64rXy4Dwoc7YjbPeNUJRgcI/wJUo30CLujk2stEFzDnKeMPR1mlHMCIFDW+KDxJ28bW59VPxwrqJFjw==", - "dev": true, - "requires": { - "@storybook/channels": "5.2.3", - "@storybook/client-logger": "5.2.3", - "@storybook/core-events": "5.2.3", - "@storybook/router": "5.2.3", - "@storybook/theming": "5.2.3", - "core-js": "^3.0.1", - "fast-deep-equal": "^2.0.1", - "global": "^4.3.2", - "lodash": "^4.17.11", - "memoizerific": "^1.11.3", - "prop-types": "^15.6.2", - "react": "^16.8.3", - "semver": "^6.0.0", - "shallow-equal": "^1.1.0", - "store2": "^2.7.1", - "telejson": "^3.0.2", - "util-deprecate": "^1.0.2" - }, - "dependencies": { - "shallow-equal": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.0.tgz", - "integrity": "sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA==", - "dev": true - } - } - }, - "@storybook/channel-postmessage": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-5.2.3.tgz", - "integrity": "sha512-ixlpr6aAYoRM72cKwEWU/W0rWzOn3mYqb/eUdIaz3Da5BtFGKm3yEFguII0l1my82uhMm5/d3UNfoh0rO3pUyg==", - "dev": true, - "requires": { - "@storybook/channels": "5.2.3", - "@storybook/client-logger": "5.2.3", - "core-js": "^3.0.1", - "global": "^4.3.2", - "telejson": "^3.0.2" - } - }, - "@storybook/channels": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-5.2.3.tgz", - "integrity": "sha512-13Mlb+XbE0mHXiLLHdg0w9byhRy/bE605U7U96PGQp2cwX4lf+4jpViO2mDCsndAFRc0+2hexXPTkwgzvZzq0A==", - "dev": true, - "requires": { - "core-js": "^3.0.1" - } - }, - "@storybook/client-api": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-5.2.3.tgz", - "integrity": "sha512-anXxcf2z+KQAk94xxdbeG1N6nTEWXj087XHQ22L3pOoX9TRzfG71UjL0/S7vj4EFUiXVHj8d6YUFwLb5LwpUIw==", - "dev": true, - "requires": { - "@storybook/addons": "5.2.3", - "@storybook/channel-postmessage": "5.2.3", - "@storybook/channels": "5.2.3", - "@storybook/client-logger": "5.2.3", - "@storybook/core-events": "5.2.3", - "@storybook/router": "5.2.3", - "common-tags": "^1.8.0", - "core-js": "^3.0.1", - "eventemitter3": "^4.0.0", - "global": "^4.3.2", - "is-plain-object": "^3.0.0", - "lodash": "^4.17.11", - "memoizerific": "^1.11.3", - "qs": "^6.6.0", - "util-deprecate": "^1.0.2" - }, - "dependencies": { + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "dev": true + }, + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + }, + "qs": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.0.tgz", + "integrity": "sha512-27RP4UotQORTpmNQDX8BHPukOnBP3p1uUJY5UnDhaJB+rMt9iMsok724XL+UHU23bEFOHRMQ2ZhI99qOWUMGFA==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "shallow-equal": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.0.tgz", + "integrity": "sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA==", + "dev": true + } + } + }, + "@storybook/addon-storysource": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-5.2.4.tgz", + "integrity": "sha512-zOJO4zt+Fj08GgegClDA6rU+VFyBb+mepfL5eJAqLtV6gSsprwZ1JNFVkyqhfrcr+vIhgRRWfuz/DDGmI+6bTQ==", + "dev": true, + "requires": { + "@storybook/addons": "5.2.4", + "@storybook/components": "5.2.4", + "@storybook/router": "5.2.4", + "@storybook/source-loader": "5.2.4", + "@storybook/theming": "5.2.4", + "core-js": "^3.0.1", + "estraverse": "^4.2.0", + "loader-utils": "^1.2.3", + "prettier": "^1.16.4", + "prop-types": "^15.7.2", + "react-syntax-highlighter": "^8.0.1", + "regenerator-runtime": "^0.12.1", + "util-deprecate": "^1.0.2" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", + "dev": true + } + } + }, + "@storybook/addon-viewport": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-5.2.4.tgz", + "integrity": "sha512-R49wSaiouSVBYeus5Xibv+XXX9Nc3/rZ1NB5yIgj658aDeuB8WgkHbM3dKd/GrWeVZWv3o4CjW81ernd3f8sdw==", + "dev": true, + "requires": { + "@storybook/addons": "5.2.4", + "@storybook/api": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/components": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/theming": "5.2.4", + "core-js": "^3.0.1", + "global": "^4.3.2", + "memoizerific": "^1.11.3", + "prop-types": "^15.7.2", + "util-deprecate": "^1.0.2" + } + }, + "@storybook/addons": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-5.2.4.tgz", + "integrity": "sha512-Q+bnVlBA308qnELxnh18hBDRSUgltR9KbV537285dUL/okv/NC6n51mxJwIaG+ksBW2wU+5e6tqSayaKF3uHLw==", + "dev": true, + "requires": { + "@storybook/api": "5.2.4", + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/core-events": "5.2.4", + "core-js": "^3.0.1", + "global": "^4.3.2", + "util-deprecate": "^1.0.2" + } + }, + "@storybook/api": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-5.2.4.tgz", + "integrity": "sha512-KqAB+NkHIHdwu749NDP+7i44jy1bFgpq7GTJlG+sx/XLZHQveK/8yn109g9bXHFth7SvdXI1+9GA/apzwBU/Mw==", + "dev": true, + "requires": { + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/router": "5.2.4", + "@storybook/theming": "5.2.4", + "core-js": "^3.0.1", + "fast-deep-equal": "^2.0.1", + "global": "^4.3.2", + "lodash": "^4.17.11", + "memoizerific": "^1.11.3", + "prop-types": "^15.6.2", + "react": "^16.8.3", + "semver": "^6.0.0", + "shallow-equal": "^1.1.0", + "store2": "^2.7.1", + "telejson": "^3.0.2", + "util-deprecate": "^1.0.2" + }, + "dependencies": { + "shallow-equal": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.0.tgz", + "integrity": "sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA==", + "dev": true + } + } + }, + "@storybook/channel-postmessage": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-5.2.4.tgz", + "integrity": "sha512-ic7/Ho8z2/aOMjoEbr5p8rijOfO3SZdJnwMvDdUxrqvYq7yACZWidPo3w2+iBwQi9HLqEsWesP1c2doJBxVGRw==", + "dev": true, + "requires": { + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "core-js": "^3.0.1", + "global": "^4.3.2", + "telejson": "^3.0.2" + } + }, + "@storybook/channels": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-5.2.4.tgz", + "integrity": "sha512-/r39yEZ5QiGdiq95DhXBypdBo7urkD3Sp1WDyK48uGkZ0gdHWSPy3BBy8OJhEhfNz7nVisTiVIBr4gIrubKDjw==", + "dev": true, + "requires": { + "core-js": "^3.0.1" + } + }, + "@storybook/client-api": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-5.2.4.tgz", + "integrity": "sha512-SOwzEFHoNapURhNqdcI7HA76o5tkWvs2+2s++i/S7xsAd3KyefIVDOdqSMlAxJkxZb8Mlrb3UNRxlrpA8SZqNA==", + "dev": true, + "requires": { + "@storybook/addons": "5.2.4", + "@storybook/channel-postmessage": "5.2.4", + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/router": "5.2.4", + "common-tags": "^1.8.0", + "core-js": "^3.0.1", + "eventemitter3": "^4.0.0", + "global": "^4.3.2", + "is-plain-object": "^3.0.0", + "lodash": "^4.17.11", + "memoizerific": "^1.11.3", + "qs": "^6.6.0", + "util-deprecate": "^1.0.2" + }, + "dependencies": { "eventemitter3": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", @@ -5398,22 +5623,22 @@ } }, "@storybook/client-logger": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-5.2.3.tgz", - "integrity": "sha512-Z1irXW4jiFs7rClgqJqYOgg5op51ynV6dVuoIqxkSC0MrOG5s/VbX7T+ojGPXKyQWD4XYGw66Hnw9jouSfXL9g==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-5.2.4.tgz", + "integrity": "sha512-ofp6QQPQZBU+RvlAH5KpZRsfAFHecCZDnl/7YG6FwjHseJr3jHTYmBGGjJDMHFHq+Q7FGQu/yVb9lMFgoQ43QQ==", "dev": true, "requires": { "core-js": "^3.0.1" } }, "@storybook/components": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-5.2.3.tgz", - "integrity": "sha512-EiWKa3xONP2BPxrssiRdvKELhF2tO14HVL131CCFY+Zg/ylExzWWWVSBun7vYcKhkI52K5lmvC1vFSsB6Gmlhw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-5.2.4.tgz", + "integrity": "sha512-APhw+XGag0RTCRJ8eCWKVr8dLt9SRqnS8LtzcZJbokCYRxRTFzhmX2eVEE1v+d0gHib1/yh2COxOjMzv3m/rQA==", "dev": true, "requires": { - "@storybook/client-logger": "5.2.3", - "@storybook/theming": "5.2.3", + "@storybook/client-logger": "5.2.4", + "@storybook/theming": "5.2.4", "@types/react-syntax-highlighter": "10.1.0", "core-js": "^3.0.1", "global": "^4.3.2", @@ -5433,9 +5658,9 @@ } }, "@storybook/core": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-5.2.3.tgz", - "integrity": "sha512-sktjYY8pH4kQGFRKjXwtwwShdG3ajjHkrnw8oh3R383MRPom7i9owx5yHHMuQedLCXIwAg84s2DzO01I2URTcg==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-5.2.4.tgz", + "integrity": "sha512-r5kDgZETNawHxpsAPw+h+pRk6l/mJhsSHeDo9/OdYtYFW7lmk2gadViXOTM+6gIWc6vQ8y750bgkahmyIIY0nQ==", "dev": true, "requires": { "@babel/plugin-proposal-class-properties": "^7.3.3", @@ -5443,15 +5668,15 @@ "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-react-constant-elements": "^7.2.0", "@babel/preset-env": "^7.4.5", - "@storybook/addons": "5.2.3", - "@storybook/channel-postmessage": "5.2.3", - "@storybook/client-api": "5.2.3", - "@storybook/client-logger": "5.2.3", - "@storybook/core-events": "5.2.3", - "@storybook/node-logger": "5.2.3", - "@storybook/router": "5.2.3", - "@storybook/theming": "5.2.3", - "@storybook/ui": "5.2.3", + "@storybook/addons": "5.2.4", + "@storybook/channel-postmessage": "5.2.4", + "@storybook/client-api": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/node-logger": "5.2.4", + "@storybook/router": "5.2.4", + "@storybook/theming": "5.2.4", + "@storybook/ui": "5.2.4", "airbnb-js-shims": "^1 || ^2", "ansi-to-html": "^0.6.11", "autoprefixer": "^9.4.9", @@ -5519,12 +5744,12 @@ } }, "ansi-to-html": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.11.tgz", - "integrity": "sha512-88XZtrcwrfkyn6fGstHnkaF1kl7hGtNCYh4vSmItgEV+6JnQHryDBf7udF4f2RhTRQmYvJvPcTtqgaqrxzc9oA==", + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.12.tgz", + "integrity": "sha512-qBkIqLW979675mP76yB7yVkzeAWtATegdnDQ0RA3CZzknx0yUlNxMSML4xFdBfTs2GWYFQ1FELfbGbVSPzJ+LA==", "dev": true, "requires": { - "entities": "^1.1.1" + "entities": "^1.1.2" } }, "body-parser": { @@ -5583,6 +5808,12 @@ "ms": "2.0.0" } }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -5922,18 +6153,18 @@ } }, "@storybook/core-events": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.2.3.tgz", - "integrity": "sha512-sZEv93yE1o+/UJdhtqQ6vo2EauZ90FjN/L8F7CR7iqDEZzqo9g77Idg9LSgcN3TAeXcGAWVSrPb1vkK7H96L2g==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.2.4.tgz", + "integrity": "sha512-nQknCmaz2S2HW6PSGcuFzve7Y1Js2Cb268vUG0ZMNtJZwFawqYc+KSQHqmOY0pVm8dyROTcWCudPA0k+hk6N5Q==", "dev": true, "requires": { "core-js": "^3.0.1" } }, "@storybook/node-logger": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-5.2.3.tgz", - "integrity": "sha512-5p+5ltLdr7cZTSCG+vdIMDLHq5AAaL/CQ/bygjl+Rw/RSpvBO5Rg8hryszFyhogToHJbn2JinUbypLA+P6tcuQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-5.2.4.tgz", + "integrity": "sha512-4OOzce02IAfrRv+Y7h3icyw6WIuDekpWF2eYjgYVVvAJYklCEwgeBTBCY0/2TJjPPTBDPUKHVP1Bdz3Vpci9pA==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -5952,17 +6183,17 @@ } }, "@storybook/react": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-5.2.3.tgz", - "integrity": "sha512-7DLUkpwyPTyDHoih/AvFr2QXQAxzXQMVDvvR5r0J0oiffLWwrqshl1TL4b16YoFvel0ZPtUdrcei6knLXhg+Wg==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-5.2.4.tgz", + "integrity": "sha512-AO0qwbD/2UGe5CrVizbaek+gCAPWkPVc0KUk38cT1mcuLpXwt1zZe7iHLQf2zOeBVSiBkPLOHrEtzDfnIJXKFQ==", "dev": true, "requires": { "@babel/plugin-transform-react-constant-elements": "^7.2.0", "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", - "@storybook/addons": "5.2.3", - "@storybook/core": "5.2.3", - "@storybook/node-logger": "5.2.3", + "@storybook/addons": "5.2.4", + "@storybook/core": "5.2.4", + "@storybook/node-logger": "5.2.4", "@svgr/webpack": "^4.0.3", "@types/webpack-env": "^1.13.7", "babel-plugin-add-react-displayname": "^0.0.5", @@ -5990,9 +6221,9 @@ } }, "@storybook/router": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-5.2.3.tgz", - "integrity": "sha512-sOu6y2GySaY82SdXfF3yOn0IJTKMqd2BDOSGEno7PWWtSenHFQWY+z99C9k0dLBTkjRes5tPcgm0OJ7RdQVRDQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-5.2.4.tgz", + "integrity": "sha512-GL7eGdj5oYST0mE9fThJB9ye9tTTgrP+aP3okZ6MeMGtNytb7bmJRpAD2E4ouuPTQVppyHI5re8g/HUxUNOT1g==", "dev": true, "requires": { "@reach/router": "^1.2.1", @@ -6013,13 +6244,13 @@ } }, "@storybook/source-loader": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-5.2.3.tgz", - "integrity": "sha512-4wKYslglYt/yC77zvRZ4OkmTVbnmnd98VMD99KP3Ap6IwkdxtGKIBTOOT36g6m0/1JaMFe+x7i+i9nbmK4hsDQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-5.2.4.tgz", + "integrity": "sha512-TXqJhj0oSnUp4w5dyh2sf3OXt/ShF9m7ceTzI8H8ZfuI8kljVsv+KvGdv+Q4f4jrO3br4Qz7bE9Wvp70i9BY1g==", "dev": true, "requires": { - "@storybook/addons": "5.2.3", - "@storybook/router": "5.2.3", + "@storybook/addons": "5.2.4", + "@storybook/router": "5.2.4", "core-js": "^3.0.1", "estraverse": "^4.2.0", "global": "^4.3.2", @@ -6064,14 +6295,14 @@ } }, "@storybook/theming": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-5.2.3.tgz", - "integrity": "sha512-3/0bo8CaoaHDYZaexydpYcwP6WW8BKRqSQBGXJY9y0TLhwY2Who5nPX9XdOLyu9d7lN//PRZlt8JnZynuncxoQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-5.2.4.tgz", + "integrity": "sha512-2ZlqBrmnm8N0352Fnu2+GB3pEsHL4Eb2eKxV0VLLgkjJuAlm7CK6+I/e4ZknQWxwYm2pQj1y6ta68A62fGBYyA==", "dev": true, "requires": { "@emotion/core": "^10.0.14", "@emotion/styled": "^10.0.14", - "@storybook/client-logger": "5.2.3", + "@storybook/client-logger": "5.2.4", "common-tags": "^1.8.0", "core-js": "^3.0.1", "deep-object-diff": "^1.1.0", @@ -6092,19 +6323,19 @@ } }, "@storybook/ui": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-5.2.3.tgz", - "integrity": "sha512-SNyo5oxupb105N4Rz8O5/iJMs/THrmdvP+vsN7CpOTxebM01rHyvk51cNUwHKG1QwlZmpXL8GbtWlbvqL2d/gQ==", - "dev": true, - "requires": { - "@storybook/addons": "5.2.3", - "@storybook/api": "5.2.3", - "@storybook/channels": "5.2.3", - "@storybook/client-logger": "5.2.3", - "@storybook/components": "5.2.3", - "@storybook/core-events": "5.2.3", - "@storybook/router": "5.2.3", - "@storybook/theming": "5.2.3", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-5.2.4.tgz", + "integrity": "sha512-zsS43k1h4bWEW6oj9FNHlUL3niHoJJ8v7iqYbRtVM12rxrYhV3K8TGVG3LCuNB75i3Be0Myy+/RHA4x9kco08A==", + "dev": true, + "requires": { + "@storybook/addons": "5.2.4", + "@storybook/api": "5.2.4", + "@storybook/channels": "5.2.4", + "@storybook/client-logger": "5.2.4", + "@storybook/components": "5.2.4", + "@storybook/core-events": "5.2.4", + "@storybook/router": "5.2.4", + "@storybook/theming": "5.2.4", "copy-to-clipboard": "^3.0.8", "core-js": "^3.0.1", "core-js-pure": "^3.0.1", @@ -6644,6 +6875,15 @@ "csstype": "^2.2.0" } }, + "@types/react-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.1.tgz", + "integrity": "sha512-J6mYm43Sid9y+OjZ7NDfJ2VVkeeuTPNVImNFITgQNXodHteKfl/t/5pAR5Z9buodZ2tCctsZjgiMlQOpfntakw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-syntax-highlighter": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-10.1.0.tgz", @@ -6687,9 +6927,9 @@ } }, "@types/webpack-env": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.14.0.tgz", - "integrity": "sha512-Fv+0gYJzE/czLoRKq+gnXWr4yBpPM3tO3C8pDLFwqVKlMICQUq5OsxwwFZYDaVr7+L6mgNDp16iOcJHEz3J5RQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.14.1.tgz", + "integrity": "sha512-0Ki9jAAhKDSuLDXOIMADg54Hu60SuBTEsWaJGGy5cV+SSUQ63J2a+RrYYGrErzz39fXzTibhKrAQJAb8M7PNcA==", "dev": true }, "@types/yargs": { @@ -6967,6 +7207,10 @@ "core-js": "^3.1.4" } }, + "@wordpress/base-styles": { + "version": "file:packages/base-styles", + "dev": true + }, "@wordpress/blob": { "version": "file:packages/blob", "requires": { @@ -6984,6 +7228,7 @@ "@wordpress/data": "file:packages/data", "@wordpress/element": "file:packages/element", "@wordpress/i18n": "file:packages/i18n", + "@wordpress/plugins": "file:packages/plugins", "lodash": "^4.17.15" } }, @@ -7255,6 +7500,7 @@ "@wordpress/jest-console": "file:packages/jest-console", "@wordpress/jest-puppeteer-axe": "file:packages/jest-puppeteer-axe", "@wordpress/scripts": "file:packages/scripts", + "@wordpress/url": "file:packages/url", "expect-puppeteer": "^4.3.0", "lodash": "^4.17.15", "uuid": "^3.3.2" @@ -7604,8 +7850,8 @@ "js-yaml": "^3.13.1", "lodash": "^4.17.15", "minimist": "^1.2.0", - "npm-package-json-lint": "^3.6.0", - "puppeteer": "^1.19.0", + "npm-package-json-lint": "^4.0.3", + "puppeteer": "^1.20.0", "read-pkg-up": "^1.0.1", "request": "^2.88.0", "resolve-bin": "^0.4.0", @@ -7779,9 +8025,9 @@ "dev": true }, "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -8056,12 +8302,20 @@ } }, "ansi-to-html": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.10.tgz", - "integrity": "sha512-znsY3gvsk4CiApWu1yVYF8Nx5Vy0FEe8B0YwyxdbCdErJu5lfKlRHB2twtUjR+dxR4WewTk2OP8XqTmWYnImOg==", + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.12.tgz", + "integrity": "sha512-qBkIqLW979675mP76yB7yVkzeAWtATegdnDQ0RA3CZzknx0yUlNxMSML4xFdBfTs2GWYFQ1FELfbGbVSPzJ+LA==", "dev": true, "requires": { - "entities": "^1.1.1" + "entities": "^1.1.2" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } } }, "ansi-wrap": { @@ -8331,14 +8585,71 @@ } }, "array.prototype.flatmap": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.1.tgz", - "integrity": "sha512-i18e2APdsiezkcqDyZor78Pbfjfds3S94dG6dgIV2ZASJaUf1N0dz2tGdrmwrmlZuNUgxH+wz6Z0zYVH2c5xzQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.2.tgz", + "integrity": "sha512-ZZtPLE74KNE+0XcPv/vQmcivxN+8FhwOLvt2udHauO0aDEpsXDQrmd5HuJGpgPVyaV8HvkDPWnJ2iaem0oCKtA==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.10.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.15.0", "function-bind": "^1.1.1" + }, + "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" + } + }, + "es-abstract": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.15.0.tgz", + "integrity": "sha512-bhkEqWJ2t2lMeaJDuk7okMkJWI/yqgH/EoGwpcvv0XW9RWQsRspI4wt6xuyuvMvvQE3gg/D9HXppgk21w78GyQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + } } }, "arrify": { @@ -9546,12 +9857,12 @@ } }, "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -9604,9 +9915,9 @@ } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", "dev": true }, "@babel/plugin-proposal-class-properties": { @@ -9641,9 +9952,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.2.tgz", - "integrity": "sha512-zZT8ivau9LOQQaOGC7bQLQOT4XPkPXgN2ERfUgk1X8ql+mVkLc4E8eKk+FO3o0154kxzqenWCorfmEXpEZcrSQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz", + "integrity": "sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -9713,9 +10024,9 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.2.tgz", - "integrity": "sha512-xBdB+XOs+lgbZc2/4F5BVDVcDNS4tcSKQc96KmlqLEAwz6tpYPEvPdmDfvVG0Ssn8lAhronaRs6Z6KSexIpK5g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.3.tgz", + "integrity": "sha512-jTkk7/uE6H2s5w6VlMHeWuH+Pcy2lmdwFoeWCVnvIrDUnB5gQqTVI8WfmEAhF2CDEarGrknZcmSFg1+bkfCoSw==", "dev": true, "requires": { "regexpu-core": "^4.6.0" @@ -9810,6 +10121,19 @@ "semver": "^5.5.0" } }, + "@babel/preset-react": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", + "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0" + } + }, "@babel/runtime": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", @@ -9831,26 +10155,26 @@ } }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -10592,7 +10916,8 @@ "buffer-from": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==" + "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", + "dev": true }, "buffer-xor": { "version": "1.0.3", @@ -10926,9 +11251,9 @@ } }, "chokidar": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.1.tgz", - "integrity": "sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -10942,599 +11267,20 @@ "normalize-path": "^3.0.0", "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", - "upath": "^1.1.0" + "upath": "^1.1.1" }, "dependencies": { - "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": 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" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true } } }, @@ -11773,12 +11519,6 @@ "is-supported-regexp-flag": "^1.0.0" } }, - "clones": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/clones/-/clones-1.2.0.tgz", - "integrity": "sha512-FXDYw4TjR8wgPZYui2LeTqWh1BLpfQ8lB6upMtlpDF6WlOOxghmTTxWyngdKTgozqBgKnHbTVwTE+hOHqAykuQ==", - "dev": true - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -12246,9 +11986,9 @@ "dev": true }, "conventional-changelog-angular": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz", - "integrity": "sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.5.tgz", + "integrity": "sha512-RrkdWnL/TVyWV1ayWmSsrWorsTDqjL/VwG5ZSEneBQrd65ONcfeA1cW7FLtNweQyMiKOyriCMTKRSlk18DjTrw==", "dev": true, "requires": { "compare-func": "^1.3.1", @@ -12349,15 +12089,15 @@ "dev": true }, "conventional-changelog-writer": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.7.tgz", - "integrity": "sha512-p/wzs9eYaxhFbrmX/mCJNwJuvvHR+j4Fd0SQa2xyAhYed6KBiZ780LvoqUUvsayP4R1DtC27czalGUhKV2oabw==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.9.tgz", + "integrity": "sha512-2Y3QfiAM37WvDMjkVNaRtZgxVzWKj73HE61YQ/95T53yle+CRwTVSl6Gbv/lWVKXeZcM5af9n9TDVf0k7Xh+cw==", "dev": true, "requires": { "compare-func": "^1.3.1", "conventional-commits-filter": "^2.0.2", "dateformat": "^3.0.0", - "handlebars": "^4.1.2", + "handlebars": "^4.4.0", "json-stringify-safe": "^5.0.1", "lodash": "^4.2.1", "meow": "^4.0.0", @@ -12460,9 +12200,9 @@ } }, "conventional-commits-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.3.tgz", - "integrity": "sha512-KaA/2EeUkO4bKjinNfGUyqPTX/6w9JGshuQRik4r/wJz7rUw3+D3fDG6sZSEqJvKILzKXFQuFkpPLclcsAuZcg==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.5.tgz", + "integrity": "sha512-qVz9+5JwdJzsbt7JbJ6P7NOXBGt8CyLFJYSjKAuPSgO+5UGfcsbk9EMR+lI8Unlvx6qwIc2YDJlrGIfay2ehNA==", "dev": true, "requires": { "JSONStream": "^1.0.4", @@ -13121,9 +12861,9 @@ "dev": true }, "schema-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.4.1.tgz", - "integrity": "sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", + "integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -13438,6 +13178,15 @@ "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", "dev": true }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, "csstype": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", @@ -13523,9 +13272,9 @@ "dev": true }, "deasync": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.14.tgz", - "integrity": "sha512-wN8sIuEqIwyQh72AG7oY6YQODCxIp1eXzEZlZznBuwDF8Q03Tdy9QNp1BNZXeadXoklNrw+Ip1fch+KXo/+ASw==", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.15.tgz", + "integrity": "sha512-pxMaCYu8cQIbGkA4Y1R0PLSooPIpH1WgFBLeJ+zLxQgHfkZG86ViJSmZmONSjZJ/R3NjwkMcIWZAzpLB2G9/CA==", "dev": true, "requires": { "bindings": "~1.2.1", @@ -14096,6 +13845,15 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.1.2" + } + }, "dom-scroll-into-view": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz", @@ -14198,9 +13956,9 @@ } }, "dotenv-expand": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-4.2.0.tgz", - "integrity": "sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "dev": true }, "dotenv-webpack": { @@ -14249,42 +14007,6 @@ "jsbn": "~0.1.0" } }, - "editorconfig": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", - "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", - "dev": true, - "requires": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" - }, - "dependencies": { - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -14695,9 +14417,9 @@ "dev": true }, "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { @@ -15965,7 +15687,8 @@ "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true }, "figures": { "version": "2.0.0", @@ -16476,14 +16199,14 @@ "dev": true }, "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -16561,12 +16284,12 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-extend": { @@ -16737,24 +16460,31 @@ } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, "needle": { - "version": "2.2.4", + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", + "debug": "^4.1.0", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.10.3", + "version": "0.12.0", "bundled": true, "dev": true, "optional": true, @@ -16782,13 +16512,13 @@ } }, "npm-bundled": { - "version": "1.0.5", + "version": "1.0.6", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.2.0", + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, @@ -16878,8 +16608,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "bundled": true, "dev": true, "optional": true } @@ -16928,7 +16657,7 @@ "optional": true }, "semver": { - "version": "5.6.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -17621,9 +17350,9 @@ } }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -18145,19 +17874,153 @@ } }, "htmlnano": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-0.2.3.tgz", - "integrity": "sha512-iS6T3J5gk2wInodbtMUyAU8sLYJOhuWDnIEd8lFRoHTypVGgawPHFEx2ZIK/XTErtDfwHBsrXeCwHAP8bdoSWw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-0.2.4.tgz", + "integrity": "sha512-wsg7+Hjyi1gHpMUixkeOjeRUNhBBTnEDB//kzvVHR+LUK4p+/31DAyE+pEACT0SQk3W0KE7Xdylk9+uNxdHXLg==", "dev": true, "requires": { - "cssnano": "^4.1.9", - "normalize-html-whitespace": "^0.2.0", + "cssnano": "^4.1.10", + "normalize-html-whitespace": "^1.0.0", "object-assign": "^4.0.1", - "posthtml": "^0.11.3", - "posthtml-render": "^1.1.4", - "svgo": "^1.0.5", - "terser": "^3.16.1", - "uncss": "^0.16.2" + "posthtml": "^0.11.4", + "posthtml-render": "^1.1.5", + "svgo": "^1.2.2", + "terser": "^4.1.2", + "uncss": "^0.17.0" + }, + "dependencies": { + "css-select": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", + "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^2.1.2", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-tree": { + "version": "1.0.0-alpha.33", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.33.tgz", + "integrity": "sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.5.3" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "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" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.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" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "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 + } + } + }, + "svgo": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.0.tgz", + "integrity": "sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.33", + "csso": "^3.5.1", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "terser": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz", + "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "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 + } + } + } } }, "htmlparser2": { @@ -18226,12 +18089,12 @@ "dev": true }, "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz", + "integrity": "sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q==", "dev": true, "requires": { - "agent-base": "^4.1.0", + "agent-base": "^4.3.0", "debug": "^3.1.0" } }, @@ -18661,9 +18524,9 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -18698,7 +18561,8 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true }, "in-publish": { "version": "2.0.0", @@ -18718,12 +18582,6 @@ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", "dev": true }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", @@ -18768,9 +18626,9 @@ }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -19183,15 +19041,6 @@ "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, - "is-path-inside": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.0.0.tgz", - "integrity": "sha512-OmUXvSq+P7aI/aRbl1dzwdlyLn8vW7Nr2/11S7y/dcLLgnQ89hgYJp7tfc+A5SRid3rNCLpruOp2CAV68/iOcA==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } - }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -20712,45 +20561,6 @@ "integrity": "sha512-O9SR2NVICx6rCqh1qsU91QZ5IoNa+2T1ROJ0OQlfvATKGmnjsAvg3r0E5ufPZ4a95jdKTPXhFWiE/sOZ7a5Rtg==", "dev": true }, - "js-beautify": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.9.1.tgz", - "integrity": "sha512-oxxvVZdOdUfzk8IOLBF2XUZvl2GoBEfA+b0of4u2EBY/46NlXasi8JdFvazA5lCrf9/lQhTjyVy2QCUW7iq0MQ==", - "dev": true, - "requires": { - "config-chain": "^1.1.12", - "editorconfig": "^0.15.2", - "glob": "^7.1.3", - "mkdirp": "~0.5.0", - "nopt": "~4.0.1" - }, - "dependencies": { - "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" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } - } - }, "js-levenshtein": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.3.tgz", @@ -21052,26 +20862,26 @@ "dev": true }, "lerna": { - "version": "3.16.4", - "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.16.4.tgz", - "integrity": "sha512-0HfwXIkqe72lBLZcNO9NMRfylh5Ng1l8tETgYQ260ZdHRbPuaLKE3Wqnd2YYRRkWfwPyEyZO8mZweBR+slVe1A==", - "dev": true, - "requires": { - "@lerna/add": "3.16.2", - "@lerna/bootstrap": "3.16.2", - "@lerna/changed": "3.16.4", - "@lerna/clean": "3.16.0", - "@lerna/cli": "3.13.0", - "@lerna/create": "3.16.0", - "@lerna/diff": "3.16.0", - "@lerna/exec": "3.16.0", - "@lerna/import": "3.16.0", - "@lerna/init": "3.16.0", - "@lerna/link": "3.16.2", - "@lerna/list": "3.16.0", - "@lerna/publish": "3.16.4", - "@lerna/run": "3.16.0", - "@lerna/version": "3.16.4", + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.18.2.tgz", + "integrity": "sha512-XznkKk2LY9HxaH+AtqyNZq7A0f9mMuv1OUeJBjJdl2oEZQPedWrmZNK6DuJNcyjjRJk6B9Xvb8eqIpe5aCHy1w==", + "dev": true, + "requires": { + "@lerna/add": "3.18.0", + "@lerna/bootstrap": "3.18.0", + "@lerna/changed": "3.18.2", + "@lerna/clean": "3.18.0", + "@lerna/cli": "3.18.0", + "@lerna/create": "3.18.0", + "@lerna/diff": "3.18.0", + "@lerna/exec": "3.18.0", + "@lerna/import": "3.18.0", + "@lerna/init": "3.18.0", + "@lerna/link": "3.18.0", + "@lerna/list": "3.18.0", + "@lerna/publish": "3.18.2", + "@lerna/run": "3.18.0", + "@lerna/version": "3.18.2", "import-local": "^2.0.0", "npmlog": "^4.1.2" } @@ -21993,15 +21803,15 @@ }, "dependencies": { "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", "dev": true }, "cacache": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.2.tgz", - "integrity": "sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -22022,15 +21832,15 @@ } }, "chownr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -22042,9 +21852,9 @@ } }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "lru-cache": { @@ -22085,9 +21895,9 @@ } }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -22118,9 +21928,9 @@ "dev": true }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } @@ -22204,6 +22014,12 @@ "unquote": "^1.1.0" } }, + "material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==", + "dev": true + }, "mathml-tag-names": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz", @@ -22314,6 +22130,12 @@ "resolved": "https://registry.npmjs.org/memize/-/memize-1.0.5.tgz", "integrity": "sha512-Dm8Jhb5kiC4+ynYsVR4QDXKt+o2dfqGuY4hE2x+XlXZkdndlT80bJxfcMv5QGp/FCy6MhG7f5ElpmKPFKOSEpg==" }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==", + "dev": true + }, "memoizerific": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", @@ -23316,9 +23138,9 @@ } }, "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true }, "mime-db": { @@ -23641,13 +23463,6 @@ "thenify-all": "^1.0.0" } }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true, - "optional": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -23750,9 +23565,9 @@ } }, "node-addon-api": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.6.2.tgz", - "integrity": "sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz", + "integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==", "dev": true }, "node-dir": { @@ -23825,9 +23640,9 @@ "dev": true }, "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", "dev": true, "requires": { "assert": "^1.1.1", @@ -23840,7 +23655,7 @@ "events": "^3.0.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", + "path-browserify": "0.0.1", "process": "^0.11.10", "punycode": "^1.2.4", "querystring-es3": "^0.2.0", @@ -23852,7 +23667,7 @@ "tty-browserify": "0.0.0", "url": "^0.11.0", "util": "^0.11.0", - "vm-browserify": "0.0.4" + "vm-browserify": "^1.0.1" }, "dependencies": { "punycode": { @@ -24357,9 +24172,9 @@ } }, "normalize-html-whitespace": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/normalize-html-whitespace/-/normalize-html-whitespace-0.2.0.tgz", - "integrity": "sha1-EBci9kI1Ucdc24+dEE/4UNrx4Q4=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz", + "integrity": "sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA==", "dev": true }, "normalize-package-data": { @@ -24416,9 +24231,9 @@ "dev": true }, "npm-lifecycle": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.2.tgz", - "integrity": "sha512-nhfOcoTHrW1lJJlM2o77vTE2RWR4YOVyj7YzmY0y5itsMjEuoJHteio/ez0BliENEPsNxIUQgwhyEW9dShj3Ww==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.4.tgz", + "integrity": "sha512-tgs1PaucZwkxECGKhC/stbEgFyc3TGh2TJcg2CDr6jbvQRdteHNhmMeljRzpe4wgFAXQADoy1cSqqi7mtiAa5A==", "dev": true, "requires": { "byline": "^5.0.0", @@ -24432,21 +24247,31 @@ }, "dependencies": { "chownr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, "node-gyp": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.0.3.tgz", - "integrity": "sha512-z/JdtkFGUm0QaQUusvloyYuGDub3nUbOo5de1Fz57cM++osBTvQatBUSTlF1k/w8vFHPxxXW6zxGvkxXSpaBkQ==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.0.5.tgz", + "integrity": "sha512-WABl9s4/mqQdZneZHVWVG4TVr6QQJZUC6PAx47ITSk9lreZ1n+7Z9mMAIbA3vnO4J9W20P7LhCxtzfWsAD/KDw==", "dev": true, "requires": { "env-paths": "^1.0.0", @@ -24458,7 +24283,7 @@ "request": "^2.87.0", "rimraf": "2", "semver": "~5.3.0", - "tar": "^4.4.8", + "tar": "^4.4.12", "which": "1" } }, @@ -24475,14 +24300,14 @@ "dev": true }, "tar": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", - "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.5", + "minipass": "^2.8.6", "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", @@ -24490,87 +24315,139 @@ } }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } }, "npm-package-arg": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", - "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { - "hosted-git-info": "^2.6.0", + "hosted-git-info": "^2.7.1", "osenv": "^0.1.5", - "semver": "^5.5.0", + "semver": "^5.6.0", "validate-npm-package-name": "^3.0.0" }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } }, "npm-package-json-lint": { - "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==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/npm-package-json-lint/-/npm-package-json-lint-4.0.3.tgz", + "integrity": "sha512-cuvTR2l5dOjjlRR3a1CCp+mh2A2HyQRxydwdcYi0Z77NRlADpf7wF3Jf8XFLGZM7J6afXNRBofBjQ1UWFyOtKA==", "dev": true, "requires": { - "ajv": "^6.9.2", + "ajv": "^6.10.2", + "ajv-errors": "^1.0.1", "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", + "cosmiconfig": "^5.2.1", + "debug": "^4.1.1", + "globby": "^10.0.1", + "ignore": "^5.1.4", + "is-plain-obj": "^2.0.0", + "log-symbols": "^3.0.0", "meow": "^5.0.0", - "plur": "^3.0.1", - "semver": "^5.6.0", - "strip-json-comments": "^2.0.1", - "validator": "^10.11.0" + "plur": "^3.1.1", + "semver": "^6.3.0", + "strip-json-comments": "^3.0.1" }, "dependencies": { - "ajv": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", - "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==", + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fill-range": "^7.0.1" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.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 + "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" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "fast-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", + "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + } + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -24581,23 +24458,135 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, "ignore": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz", - "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", "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==", + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.0.0.tgz", + "integrity": "sha512-EYisGhpgSCwspmIuRHGjROWTon2Xp8Z7U03Wubk/bTL5TTRC5R1rGVgyjzBrk9+ULdH6cRD06KRcw/xfqhVYKQ==", + "dev": true + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2" + } + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } } } }, @@ -24612,9 +24601,9 @@ } }, "npm-pick-manifest": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", - "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -24623,9 +24612,9 @@ }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -24676,6 +24665,12 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, + "nwsapi": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", + "dev": true + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -25197,28 +25192,28 @@ } }, "parcel-bundler": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/parcel-bundler/-/parcel-bundler-1.12.3.tgz", - "integrity": "sha512-8bq6lj0hhQeGxD9f9xEkFMXQ3d8TIlf2+isKxoi9bciB0KVEILRGllaPkUgp++5t0anToBh9+tG6ZyInXOC1/A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0 <7.4.0", - "@babel/core": "^7.0.0 <7.4.0", - "@babel/generator": "^7.0.0 <7.4.0", - "@babel/parser": "^7.0.0 <7.4.0", - "@babel/plugin-transform-flow-strip-types": "^7.0.0 <7.4.0", - "@babel/plugin-transform-modules-commonjs": "^7.0.0 <7.4.0", - "@babel/plugin-transform-react-jsx": "^7.0.0 <7.4.0", - "@babel/preset-env": "^7.0.0 <7.4.0", - "@babel/runtime": "^7.0.0 <7.4.0", - "@babel/template": "^7.0.0 <7.4.0", - "@babel/traverse": "^7.0.0 <7.4.0", - "@babel/types": "^7.0.0 <7.4.0", + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/parcel-bundler/-/parcel-bundler-1.12.4.tgz", + "integrity": "sha512-G+iZGGiPEXcRzw0fiRxWYCKxdt/F7l9a0xkiU4XbcVRJCSlBnioWEwJMutOCCpoQmaQtjB4RBHDGIHN85AIhLQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/core": "^7.4.4", + "@babel/generator": "^7.4.4", + "@babel/parser": "^7.4.4", + "@babel/plugin-transform-flow-strip-types": "^7.4.4", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/preset-env": "^7.4.4", + "@babel/runtime": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4", "@iarna/toml": "^2.2.0", "@parcel/fs": "^1.11.0", - "@parcel/logger": "^1.11.0", + "@parcel/logger": "^1.11.1", "@parcel/utils": "^1.11.0", - "@parcel/watcher": "^1.12.0", + "@parcel/watcher": "^1.12.1", "@parcel/workers": "^1.11.0", "ansi-to-html": "^0.6.4", "babylon-walk": "^1.0.2", @@ -25227,12 +25222,14 @@ "clone": "^2.1.1", "command-exists": "^1.2.6", "commander": "^2.11.0", + "core-js": "^2.6.5", "cross-spawn": "^6.0.4", "css-modules-loader-core": "^1.1.0", "cssnano": "^4.0.0", "deasync": "^0.1.14", "dotenv": "^5.0.0", - "dotenv-expand": "^4.2.0", + "dotenv-expand": "^5.1.0", + "envinfo": "^7.3.1", "fast-glob": "^2.2.2", "filesize": "^3.6.0", "get-port": "^3.2.0", @@ -25253,7 +25250,7 @@ "posthtml-render": "^1.1.3", "resolve": "^1.4.0", "semver": "^5.4.1", - "serialize-to-js": "^1.1.1", + "serialize-to-js": "^3.0.0", "serve-static": "^1.12.4", "source-map": "0.6.1", "terser": "^3.7.3", @@ -25261,180 +25258,14 @@ "ws": "^5.1.1" }, "dependencies": { - "@babel/core": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.3.4.tgz", - "integrity": "sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.3.4", - "@babel/helpers": "^7.2.0", - "@babel/parser": "^7.3.4", - "@babel/template": "^7.2.2", - "@babel/traverse": "^7.3.4", - "@babel/types": "^7.3.4", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.11", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.4.tgz", - "integrity": "sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.3.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/parser": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.4.tgz", - "integrity": "sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==", - "dev": true - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz", - "integrity": "sha512-V6y0uaUQrQPXUrmj+hgnks8va2L0zcZymeU7TtWEgdRLNkceafKXEduv7QzgQAE4lT+suwooG9dC7LFhdRAbVQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0" - } - }, - "@babel/preset-env": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.4.tgz", - "integrity": "sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==", + "@babel/plugin-transform-flow-strip-types": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.6.3.tgz", + "integrity": "sha512-l0ETkyEofkqFJ9LS6HChNIKtVJw2ylKbhYMlJ5C6df+ldxxaLIyXY4yOdDQQspfFpV8/vDiaWoJlvflstlYNxg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.3.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.3.4", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.3.4", - "@babel/plugin-transform-classes": "^7.3.4", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.2.0", - "@babel/plugin-transform-dotall-regex": "^7.2.0", - "@babel/plugin-transform-duplicate-keys": "^7.2.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.2.0", - "@babel/plugin-transform-function-name": "^7.2.0", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.2.0", - "@babel/plugin-transform-modules-systemjs": "^7.3.4", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.3.0", - "@babel/plugin-transform-new-target": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.2.0", - "@babel/plugin-transform-parameters": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.3.4", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.2.0", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.2.0", - "browserslist": "^4.3.4", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.3.0" - } - }, - "@babel/runtime": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", - "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.12.0" - } - }, - "@babel/template": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", - "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.2.2", - "@babel/types": "^7.2.2" - } - }, - "@babel/traverse": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.3.4.tgz", - "integrity": "sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.3.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.3.4", - "@babel/types": "^7.3.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz", - "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" + "@babel/plugin-syntax-flow": "^7.2.0" } }, "clone": { @@ -25443,6 +25274,12 @@ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", "dev": true }, + "core-js": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==", + "dev": true + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -25456,21 +25293,6 @@ "which": "^1.2.9" } }, - "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" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -25480,28 +25302,16 @@ "minimist": "^1.2.0" } }, - "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 - }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", - "dev": true - }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "source-map": { @@ -25629,9 +25439,9 @@ "dev": true }, "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", "dev": true }, "path-dirname": { @@ -25651,12 +25461,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -25859,9 +25663,9 @@ } }, "plur": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/plur/-/plur-3.0.1.tgz", - "integrity": "sha512-lJl0ojUynAM1BZn58Pas2WT/TXeC1+bS+UqShl0x9+49AtOn7DixRXVzaC8qrDOIxNDmepKnLuMTH7NQmkX0PA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz", + "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==", "dev": true, "requires": { "irregular-plurals": "^2.0.0" @@ -26828,36 +26632,13 @@ "dev": true }, "posthtml": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.11.3.tgz", - "integrity": "sha512-quMHnDckt2DQ9lRi6bYLnuyBDnVzK+McHa8+ar4kTdYbWEo/92hREOu3h70ZirudOOp/my2b3r0m5YtxY52yrA==", + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.11.6.tgz", + "integrity": "sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==", "dev": true, "requires": { - "object-assign": "^4.1.1", - "posthtml-parser": "^0.3.3", - "posthtml-render": "^1.1.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "posthtml-parser": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.3.3.tgz", - "integrity": "sha512-H/Z/yXGwl49A7hYQLV1iQ3h87NE0aZ/PMZhFwhw3lKeCAN+Ti4idrHvVvh4/GX10I7u77aQw+QB4vV5/Lzvv5A==", - "dev": true, - "requires": { - "htmlparser2": "^3.9.2", - "isobject": "^2.1.0", - "object-assign": "^4.1.1" - } - } + "posthtml-parser": "^0.4.1", + "posthtml-render": "^1.1.5" } }, "posthtml-parser": { @@ -26871,9 +26652,9 @@ } }, "posthtml-render": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.1.4.tgz", - "integrity": "sha512-jL6eFIzoN3xUEvbo33OAkSDE2VIKU4JQ1wENOows1DpfnrdapR/K3Q1/fB43Mq7wQlcSgRm23nFrvoioufM7eA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.1.5.tgz", + "integrity": "sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w==", "dev": true }, "prelude-ls": { @@ -27276,9 +27057,9 @@ "dev": true }, "puppeteer": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.19.0.tgz", - "integrity": "sha512-2S6E6ygpoqcECaagDbBopoSOPDv0pAZvTbnBgUY+6hq0/XDFDOLEMNlHF/SKJlzcaZ9ckiKjKDuueWI3FN/WXw==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz", + "integrity": "sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==", "dev": true, "requires": { "debug": "^4.1.0", @@ -27514,6 +27295,20 @@ "@babel/runtime": "^7.0.0" } }, + "react-color": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.3.tgz", + "integrity": "sha512-1dtO8LqAVotPIChlmo6kLtFS1FP89ll8/OiA8EcFRDR+ntcK+0ukJgByuIQHRtzvigf26dV5HklnxDIvhON9VQ==", + "dev": true, + "requires": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.11", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + } + }, "react-dates": { "version": "17.2.0", "resolved": "https://registry.npmjs.org/react-dates/-/react-dates-17.2.0.tgz", @@ -27941,6 +27736,15 @@ "prop-types": "^15.6.1" } }, + "react-input-autosize": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz", + "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==", + "dev": true, + "requires": { + "prop-types": "^15.5.8" + } + }, "react-is": { "version": "16.8.4", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.4.tgz", @@ -28535,9 +28339,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz", - "integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz", + "integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==", "dev": true, "requires": { "regenerator-runtime": "^0.13.2" @@ -28566,10 +28370,26 @@ "integrity": "sha512-ITw8t/HOFNose2yf1y9pPFSSeB9ISOq2JdHpuZvj/Qb+iSsLml8GkkHdDlURzieO7B3dFDtMrrneZLl3N5z/hg==", "dev": true }, + "react-select": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.0.8.tgz", + "integrity": "sha512-v9LpOhckLlRmXN5A6/mGGEft4FMrfaBFTGAnuPHcUgVId7Je42kTq9y0Z+Ye5z8/j0XDT3zUqza8gaRaI1PZIg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/cache": "^10.0.9", + "@emotion/core": "^10.0.9", + "@emotion/css": "^10.0.9", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-input-autosize": "^2.2.2", + "react-transition-group": "^2.2.1" + } + }, "react-sizeme": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-2.6.7.tgz", - "integrity": "sha512-xCjPoBP5jmeW58TxIkcviMZqabZis7tTvDFWf0/Wa5XCgVWQTIe74NQBes2N1Kmp64GRLkpm60BaP0kk+v8aCQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-2.6.8.tgz", + "integrity": "sha512-eJKHV226d/S3st2He7bLIlY7FAmi2ItvZmUCmLLNjIvYjtiv58BksuFhTBQmvAxWaXZGb3Ao/44wfAS1voRdjA==", "dev": true, "requires": { "element-resize-detector": "^1.1.15", @@ -28640,6 +28460,18 @@ "react-proxy": "^1.1.7" } }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dev": true, + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, "react-with-direction": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/react-with-direction/-/react-with-direction-1.3.0.tgz", @@ -28675,6 +28507,15 @@ "global-cache": "^1.2.1" } }, + "reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "dev": true, + "requires": { + "lodash": "^4.0.1" + } + }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -28685,18 +28526,18 @@ } }, "read-cmd-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz", - "integrity": "sha1-LV0Vd4ajfAVdIgd8MsU/gynpHHs=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.4.tgz", + "integrity": "sha512-Pqpl3qJ/QdOIjRYA0q5DND/gLvGOfpIz/fYVDGYpOXfW/lFrIttmLsBnd6IkyK10+JHU9zhsaudfvrQTBB9YFQ==", "dev": true, "requires": { "graceful-fs": "^4.1.2" } }, "read-package-json": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.0.13.tgz", - "integrity": "sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.0.tgz", + "integrity": "sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A==", "dev": true, "requires": { "glob": "^7.1.1", @@ -28958,202 +28799,6 @@ "safe-regex": "^1.1.0" } }, - "regexp-tree": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.1.tgz", - "integrity": "sha512-HwRjOquc9QOwKTgbxvZTcddS5mlNlwePMQ3NFL8broajMLD5CXDAqas8Y5yxJH5QtZp5iRor3YCILd5pz71Cgw==", - "dev": true, - "requires": { - "cli-table3": "^0.5.0", - "colors": "^1.1.2", - "yargs": "^12.0.5" - }, - "dependencies": { - "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 - }, - "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", - "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": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } - } - }, - "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" - } - }, - "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 - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.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 - }, - "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" - } - }, - "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": { - "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" - } - }, - "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": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, "regexp.prototype.flags": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", @@ -29277,12 +28922,12 @@ } }, "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", + "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -29300,9 +28945,9 @@ } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", + "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==", "dev": true }, "@babel/plugin-proposal-object-rest-spread": { @@ -29327,26 +28972,26 @@ } }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -29935,15 +29580,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "safer-eval": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/safer-eval/-/safer-eval-1.3.5.tgz", - "integrity": "sha512-BJ//K2Y+EgCbOHEsDGS5YahYBcYy7JcFpKDo2ba5t4MnOGHYtk7HvQkcxTDFvjQvJ0CRcdas/PyF+gTTCay+3w==", - "dev": true, - "requires": { - "clones": "^1.2.0" - } - }, "sane": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", @@ -30156,6 +29792,15 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dev": true, + "requires": { + "xmlchars": "^2.1.1" + } + }, "scheduler": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", @@ -30265,14 +29910,10 @@ "dev": true }, "serialize-to-js": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-1.2.2.tgz", - "integrity": "sha512-mUc8vA5iJghe+O+3s0YDGFLMJcqitVFk787YKiv8a4sf6RX5W0u81b+gcHrp15O0fFa010dRBVZvwcKXOWsL9Q==", - "dev": true, - "requires": { - "js-beautify": "^1.8.9", - "safer-eval": "^1.3.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.0.0.tgz", + "integrity": "sha512-WdGgi0jGnWCQXph2p3vkxceDnTfvfyXfYxherQMRcZjSaJzMQdMBAW6i0nojsBKIZ3fFOztZKKVbbm05VbIdRA==", + "dev": true }, "serve-favicon": { "version": "2.5.0", @@ -30492,12 +30133,6 @@ } } }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", - "dev": true - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -30790,6 +30425,17 @@ "requires": { "agent-base": "~4.2.1", "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } } }, "sort-keys": { @@ -30804,7 +30450,8 @@ "source-list-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true }, "source-map": { "version": "0.5.7", @@ -32251,69 +31898,11 @@ "source-map-support": "~0.5.10" }, "dependencies": { - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "terser": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.1.tgz", - "integrity": "sha512-pnzH6dnFEsR2aa2SJaKb1uSCl3QmIsJ8dEkj0Fky+2AwMMcC9doMqLOQIH6wVTEKaVfKVvLSk5qxPBEZT9mywg==", - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "requires": { - "unique-slug": "^2.0.0" - } - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -33180,42 +32769,146 @@ "dev": true }, "uncss": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.16.2.tgz", - "integrity": "sha1-OyJpxZAS2nxmy+mPvt3e75TwZJw=", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.17.2.tgz", + "integrity": "sha512-hu2HquwDItuGDem4YsJROdAD8SknmWtM24zwhQax6J1se8tPjV1cnwPKhtjodzBaUhaL8Zb3hlGdZ2WAUpbAOg==", "dev": true, "requires": { - "commander": "^2.9.0", - "glob": "^7.0.3", - "is-absolute-url": "^2.0.0", - "is-html": "^1.0.0", - "jsdom": "^11.3.0", - "lodash": "^4.13.1", - "postcss": "^6.0.14", - "postcss-selector-parser": "3.1.1", - "request": "^2.72.0" + "commander": "^2.20.0", + "glob": "^7.1.4", + "is-absolute-url": "^3.0.1", + "is-html": "^1.1.0", + "jsdom": "^14.1.0", + "lodash": "^4.17.15", + "postcss": "^7.0.17", + "postcss-selector-parser": "6.0.2", + "request": "^2.88.0" }, "dependencies": { - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "abab": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz", + "integrity": "sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg==", + "dev": true + }, + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", "dev": true, "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" } }, - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", "dev": true, "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "glob": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", + "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" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "jsdom": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz", + "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.3", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + }, + "postcss": { + "version": "7.0.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.18.tgz", + "integrity": "sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" } }, "source-map": { @@ -33223,6 +32916,60 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "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" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } } } }, @@ -33347,6 +33094,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, "requires": { "imurmurhash": "^0.1.4" } @@ -33513,12 +33261,6 @@ } } }, - "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", - "dev": true - }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -33600,9 +33342,9 @@ "dev": true }, "schema-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.4.1.tgz", - "integrity": "sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", + "integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -33685,9 +33427,9 @@ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "v8-compile-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", - "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", "dev": true }, "validate-npm-package-license": { @@ -33709,12 +33451,6 @@ "builtins": "^1.0.3" } }, - "validator": { - "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": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -33786,13 +33522,10 @@ "dev": true }, "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", + "dev": true }, "w3c-hr-time": { "version": "1.0.1", @@ -33803,6 +33536,17 @@ "browser-process-hrtime": "^0.1.2" } }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dev": true, + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, "wait-on": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-3.3.0.tgz", @@ -34773,9 +34517,9 @@ }, "dependencies": { "graceful-fs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", - "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "make-dir": { @@ -34795,9 +34539,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -34886,6 +34630,12 @@ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", "dev": true }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xmldoc": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-0.4.0.tgz", diff --git a/package.json b/package.json index 31d63d691e3b5c..87179bff38b782 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "6.6.0", + "version": "6.8.0-rc.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", @@ -72,22 +72,24 @@ "@babel/runtime-corejs3": "7.4.5", "@babel/traverse": "7.4.5", "@octokit/rest": "16.26.0", - "@storybook/addon-a11y": "5.2.3", - "@storybook/addon-docs": "5.2.3", - "@storybook/addon-storysource": "5.2.3", - "@storybook/addon-viewport": "5.2.3", - "@storybook/react": "5.2.3", + "@storybook/addon-a11y": "5.2.4", + "@storybook/addon-docs": "5.2.4", + "@storybook/addon-knobs": "5.2.4", + "@storybook/addon-storysource": "5.2.4", + "@storybook/addon-viewport": "5.2.4", + "@storybook/react": "5.2.4", "@wordpress/babel-plugin-import-jsx-pragma": "file:packages/babel-plugin-import-jsx-pragma", "@wordpress/babel-plugin-makepot": "file:packages/babel-plugin-makepot", "@wordpress/babel-preset-default": "file:packages/babel-preset-default", + "@wordpress/base-styles": "file:packages/base-styles", "@wordpress/browserslist-config": "file:packages/browserslist-config", "@wordpress/custom-templated-path-webpack-plugin": "file:packages/custom-templated-path-webpack-plugin", "@wordpress/dependency-extraction-webpack-plugin": "file:packages/dependency-extraction-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", "@wordpress/env": "file:packages/env", + "@wordpress/eslint-plugin": "file:packages/eslint-plugin", "@wordpress/jest-console": "file:packages/jest-console", "@wordpress/jest-preset-default": "file:packages/jest-preset-default", "@wordpress/jest-puppeteer-axe": "file:packages/jest-puppeteer-axe", @@ -121,7 +123,7 @@ "jest-junit": "6.4.0", "jest-serializer-enzyme": "1.0.0", "jsdom": "11.12.0", - "lerna": "3.16.4", + "lerna": "3.18.2", "lint-staged": "9.2.5", "lodash": "4.17.15", "make-dir": "3.0.0", @@ -130,7 +132,7 @@ "mkdirp": "0.5.1", "node-sass": "4.12.0", "node-watch": "0.6.0", - "parcel-bundler": "1.12.3", + "parcel-bundler": "1.12.4", "postcss": "7.0.13", "progress": "2.0.3", "react": "16.9.0", @@ -153,34 +155,6 @@ "webpack": "4.41.0", "worker-farm": "1.7.0" }, - "npmPackageJsonLintConfig": { - "extends": "@wordpress/npm-package-json-lint-config", - "rules": { - "description-format": [ - "error", - { - "requireCapitalFirstLetter": true, - "requireEndingPeriod": true - } - ], - "require-publishConfig": "error", - "require-repository-directory": "error", - "valid-values-author": [ - "error", - [ - "The WordPress Contributors" - ] - ], - "valid-values-publishConfig": [ - "error", - [ - { - "access": "public" - } - ] - ] - } - }, "scripts": { "prebuild": "npm run check-engines", "clean:packages": "rimraf ./packages/*/build ./packages/*/build-module ./packages/*/build-style ./packages/*/node_modules", @@ -203,7 +177,7 @@ "lint-js": "wp-scripts lint-js", "lint-js:fix": "npm run lint-js -- --fix", "lint-php": "wp-scripts env lint-php", - "lint-pkg-json": "wp-scripts lint-pkg-json ./packages", + "lint-pkg-json": "wp-scripts lint-pkg-json . 'packages/*/package.json'", "lint-css": "wp-scripts lint-style '**/*.scss'", "lint-css:fix": "npm run lint-css -- --fix", "lint-types": "tsc", @@ -213,9 +187,7 @@ "publish:dev": "npm run build:packages && lerna publish --npm-tag next", "publish:prod": "npm run build:packages && lerna publish", "test": "npm run lint && npm run test-unit", - "pretest-e2e": "npm run env reinstall", "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", - "test-e2e:local": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", "test-e2e:watch": "npm run test-e2e:local -- --watch", "test-performance": "wp-scripts test-e2e --config packages/e2e-tests/jest.performance.config.js", "test-php": "npm run lint-php && npm run test-unit-php", @@ -227,7 +199,7 @@ "test-unit:native": "cd test/native/ && cross-env NODE_ENV=test jest --config ./jest.config.js", "test-unit:native:debug": "cd test/native/ && node --inspect ../../node_modules/.bin/jest --runInBand --config ./jest.config.js", "design-system:build": "npm run build:packages && build-storybook -c ./packages/components/storybook -o ./playground/dist/design-system/components", - "design-system:dev": "concurrently \"npm run dev:packages\" \"start-storybook -c ./packages/components/storybook\"", + "design-system:dev": "npm run build:packages && concurrently \"npm run dev:packages\" \"start-storybook -c ./packages/components/storybook\"", "playground:build": "npm run build:packages && parcel build playground/src/index.html -d playground/dist", "playground:dev": "concurrently \"npm run dev:packages\" \"parcel playground/src/index.html -d playground/dist\"", "preenv": "npm run check-engines", diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 546abcd609a520..eb2f11f3a971d8 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/a11y", - "version": "2.5.0", + "version": "2.5.1", "description": "Accessibility (a11y) utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 62608fae8a7b73..2f08cd7c0901bd 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "1.7.0", + "version": "1.7.2", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 26876a3e860239..462c964c370c6b 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "3.6.0", + "version": "3.6.3", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/src/index.js b/packages/api-fetch/src/index.js index 7535d2117b2f24..dc7dda91fa1e60 100644 --- a/packages/api-fetch/src/index.js +++ b/packages/api-fetch/src/index.js @@ -1,8 +1,3 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; - /** * Internal dependencies */ @@ -13,6 +8,8 @@ import fetchAllMiddleware from './middlewares/fetch-all-middleware'; import namespaceEndpointMiddleware from './middlewares/namespace-endpoint'; import httpV1Middleware from './middlewares/http-v1'; import userLocaleMiddleware from './middlewares/user-locale'; +import mediaUploadMiddleware from './middlewares/media-upload'; +import { parseResponseAndNormalizeError, parseAndThrowError } from './utils/response'; /** * Default set of header values which should be sent with every request unless @@ -80,48 +77,10 @@ const defaultFetchHandler = ( nextOptions ) => { } ); - const parseResponse = ( response ) => { - if ( parse ) { - if ( response.status === 204 ) { - return null; - } - - return response.json ? response.json() : Promise.reject( response ); - } - - return response; - }; - return responsePromise .then( checkStatus ) - .then( parseResponse ) - .catch( ( response ) => { - if ( ! parse ) { - throw response; - } - - const invalidJsonError = { - code: 'invalid_json', - message: __( 'The response is not a valid JSON response.' ), - }; - - if ( ! response || ! response.json ) { - throw invalidJsonError; - } - - return response.json() - .catch( () => { - throw invalidJsonError; - } ) - .then( ( error ) => { - const unknownError = { - code: 'unknown_error', - message: __( 'An unknown error occurred.' ), - }; - - throw error || unknownError; - } ); - } ); + .catch( ( response ) => parseAndThrowError( response, parse ) ) + .then( ( response ) => parseResponseAndNormalizeError( response, parse ) ); }; let fetchHandler = defaultFetchHandler; @@ -179,5 +138,6 @@ apiFetch.createNonceMiddleware = createNonceMiddleware; apiFetch.createPreloadingMiddleware = createPreloadingMiddleware; apiFetch.createRootURLMiddleware = createRootURLMiddleware; apiFetch.fetchAllMiddleware = fetchAllMiddleware; +apiFetch.mediaUploadMiddleware = mediaUploadMiddleware; export default apiFetch; diff --git a/packages/api-fetch/src/middlewares/media-upload.js b/packages/api-fetch/src/middlewares/media-upload.js new file mode 100644 index 00000000000000..6772aaf3ded733 --- /dev/null +++ b/packages/api-fetch/src/middlewares/media-upload.js @@ -0,0 +1,74 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { + parseAndThrowError, + parseResponseAndNormalizeError, +} from '../utils/response'; + +/** + * Middleware handling media upload failures and retries. + * + * @param {Object} options Fetch options. + * @param {Function} next [description] + * + * @return {*} The evaluated result of the remaining middleware chain. + */ +function mediaUploadMiddleware( options, next ) { + const isMediaUploadRequest = + ( options.path && options.path.indexOf( '/wp/v2/media' ) !== -1 ) || + ( options.url && options.url.indexOf( '/wp/v2/media' ) !== -1 ); + + if ( ! isMediaUploadRequest ) { + return next( options, next ); + } + let retries = 0; + const maxRetries = 5; + + const postProcess = ( attachmentId ) => { + retries++; + return next( { + path: `/wp/v2/media/${ attachmentId }/post-process`, + method: 'POST', + data: { action: 'create-image-subsizes' }, + parse: false, + } ) + .catch( () => { + if ( retries < maxRetries ) { + return postProcess( attachmentId ); + } + next( { + path: `/wp/v2/media/${ attachmentId }?force=true`, + method: 'DELETE', + } ); + + return Promise.reject(); + } ); + }; + + return next( { ...options, parse: false } ) + .catch( ( response ) => { + const attachmentId = response.headers.get( 'x-wp-upload-attachment-id' ); + if ( response.status >= 500 && response.status < 600 && attachmentId ) { + return postProcess( attachmentId ).catch( () => { + if ( options.parse !== false ) { + return Promise.reject( { + code: 'post_process', + message: __( 'Media upload failed. If this is a photo or a large image, please scale it down and try again.' ), + } ); + } + + return Promise.reject( response ); + } ); + } + return parseAndThrowError( response, options.parse ); + } ) + .then( ( response ) => parseResponseAndNormalizeError( response, options.parse ) ); +} + +export default mediaUploadMiddleware; diff --git a/packages/api-fetch/src/utils/response.js b/packages/api-fetch/src/utils/response.js new file mode 100644 index 00000000000000..bb7b79ce2f8254 --- /dev/null +++ b/packages/api-fetch/src/utils/response.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Parses the apiFetch response. + * + * @param {Response} response + * @param {boolean} shouldParseResponse + * + * @return {Promise} Parsed response + */ +const parseResponse = ( response, shouldParseResponse = true ) => { + if ( shouldParseResponse ) { + if ( response.status === 204 ) { + return null; + } + + return response.json ? response.json() : Promise.reject( response ); + } + + return response; +}; + +const parseJsonAndNormalizeError = ( response ) => { + const invalidJsonError = { + code: 'invalid_json', + message: __( 'The response is not a valid JSON response.' ), + }; + + if ( ! response || ! response.json ) { + throw invalidJsonError; + } + + return response.json() + .catch( () => { + throw invalidJsonError; + } ); +}; + +/** + * Parses the apiFetch response properly and normalize response errors. + * + * @param {Response} response + * @param {boolean} shouldParseResponse + * + * @return {Promise} Parsed response. + */ +export const parseResponseAndNormalizeError = ( response, shouldParseResponse = true ) => { + return Promise.resolve( parseResponse( response, shouldParseResponse ) ) + .catch( ( res ) => parseAndThrowError( res, shouldParseResponse ) ); +}; + +export function parseAndThrowError( response, shouldParseResponse = true ) { + if ( ! shouldParseResponse ) { + throw response; + } + + return parseJsonAndNormalizeError( response ) + .then( ( error ) => { + const unknownError = { + code: 'unknown_error', + message: __( 'An unknown error occurred.' ), + }; + + throw error || unknownError; + } ); +} + diff --git a/packages/autop/package.json b/packages/autop/package.json index 0f63cb48560d3c..ef57d2a3a0516b 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/autop", - "version": "2.5.0", + "version": "2.5.1", "description": "WordPress's automatic paragraph functions `autop` and `removep`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index e8f3df29218a58..c02981fc1316cc 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-preset-default", - "version": "4.6.0", + "version": "4.6.2", "description": "Default Babel preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/base-styles/.npmrc b/packages/base-styles/.npmrc new file mode 100644 index 00000000000000..43c97e719a5a82 --- /dev/null +++ b/packages/base-styles/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/base-styles/README.md b/packages/base-styles/README.md new file mode 100644 index 00000000000000..20945fd08acf8f --- /dev/null +++ b/packages/base-styles/README.md @@ -0,0 +1,51 @@ +# Base Styles + +Base SCSS utilities and variables for WordPress. + +## Installation + +Install the module + +```bash +npm install @wordpress/base-styles --save-dev +``` + +## Use + +### SCSS utilities and variables + +In your application's SCSS file, include styles like so: + +```scss +@import "node_modules/@wordpress/base-styles/colors"; +@import "node_modules/@wordpress/base-styles/variables"; +@import "node_modules/@wordpress/base-styles/mixins"; +@import "node_modules/@wordpress/base-styles/breakpoints"; +@import "node_modules/@wordpress/base-styles/animations"; +@import "node_modules/@wordpress/base-styles/z-index"; +``` + +If you use [Webpack](https://webpack.js.org/) for your SCSS pipeline, you can use `~` to resolve to `node_modules`: + +```scss +@import "~@wordpress/base-styles/colors"; +``` + +To make that work with [`sass`](https://www.npmjs.com/package/sass) or [`node-sass`](https://www.npmjs.com/package/node-sass) NPM modules without Webpack, you'd have to use [includePaths option](https://sass-lang.com/documentation/js-api#includepaths): + +```json +{ + "includePaths": ["node_modules"] +} +``` + +### PostCSS color schemes + +To use color schemes with [`@wordpress/postcss-themes`](https://www.npmjs.com/package/@wordpress/postcss-themes), import them like so: + +```js +const { adminColorSchemes } = require( '@wordpress/base-styles' ); +const wpPostcss = require( '@wordpress/postcss-themes' )( adminColorSchemes ) +``` + +

Code is Poetry.

diff --git a/assets/stylesheets/_animations.scss b/packages/base-styles/_animations.scss similarity index 100% rename from assets/stylesheets/_animations.scss rename to packages/base-styles/_animations.scss diff --git a/assets/stylesheets/_breakpoints.scss b/packages/base-styles/_breakpoints.scss similarity index 100% rename from assets/stylesheets/_breakpoints.scss rename to packages/base-styles/_breakpoints.scss diff --git a/assets/stylesheets/_colors.scss b/packages/base-styles/_colors.scss similarity index 100% rename from assets/stylesheets/_colors.scss rename to packages/base-styles/_colors.scss diff --git a/assets/stylesheets/_mixins.scss b/packages/base-styles/_mixins.scss similarity index 100% rename from assets/stylesheets/_mixins.scss rename to packages/base-styles/_mixins.scss diff --git a/assets/stylesheets/_variables.scss b/packages/base-styles/_variables.scss similarity index 100% rename from assets/stylesheets/_variables.scss rename to packages/base-styles/_variables.scss diff --git a/assets/stylesheets/_z-index.scss b/packages/base-styles/_z-index.scss similarity index 98% rename from assets/stylesheets/_z-index.scss rename to packages/base-styles/_z-index.scss index 7b7e0aedd55301..36cc265a0b7f64 100644 --- a/assets/stylesheets/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -24,6 +24,7 @@ $z-layers: ( ".block-editor-warning": 5, ".block-library-gallery-item__inline-menu": 20, ".block-editor-url-input__suggestions": 30, + ".edit-post-layout__footer": 30, ".edit-post-header": 30, ".edit-widgets-header": 30, ".block-library-button__inline-link .block-editor-url-input__suggestions": 6, // URL suggestions for button block above sibling inserter @@ -31,6 +32,7 @@ $z-layers: ( ".wp-block-cover__inner-container": 1, // InnerBlocks area inside cover image block ".wp-block-cover.has-background-dim::before": 1, // Overlay area inside block cover need to be higher than the video background. ".wp-block-cover__video-background": 0, // Video background inside cover block. + ".wp-block-site-title__save-button": 1, // Active pill button ".components-button.is-button {:focus or .is-primary}": 1, diff --git a/packages/base-styles/index.js b/packages/base-styles/index.js new file mode 100644 index 00000000000000..bac5dede479a12 --- /dev/null +++ b/packages/base-styles/index.js @@ -0,0 +1,60 @@ +exports.adminColorSchemes = { + defaults: { + primary: '#0085ba', + secondary: '#11a0d2', + toggle: '#11a0d2', + button: '#007cba', + outlines: '#007cba', + }, + themes: { + 'admin-color-light': { + primary: '#0085ba', + secondary: '#c75726', + toggle: '#11a0d2', + button: '#0085ba', + outlines: '#007cba', + }, + 'admin-color-blue': { + primary: '#82b4cb', + secondary: '#d9ab59', + toggle: '#82b4cb', + button: '#d9ab59', + outlines: '#417e9B', + }, + 'admin-color-coffee': { + primary: '#c2a68c', + secondary: '#9fa47b', + toggle: '#c2a68c', + button: '#c2a68c', + outlines: '#59524c', + }, + 'admin-color-ectoplasm': { + primary: '#a7b656', + secondary: '#c77430', + toggle: '#a7b656', + button: '#a7b656', + outlines: '#523f6d', + }, + 'admin-color-midnight': { + primary: '#e14d43', + secondary: '#77a6b9', + toggle: '#77a6b9', + button: '#e14d43', + outlines: '#497b8d', + }, + 'admin-color-ocean': { + primary: '#a3b9a2', + secondary: '#a89d8a', + toggle: '#a3b9a2', + button: '#a3b9a2', + outlines: '#5e7d5e', + }, + 'admin-color-sunrise': { + primary: '#d1864a', + secondary: '#c8b03c', + toggle: '#c8b03c', + button: '#d1864a', + outlines: '#837425', + }, + }, +}; diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json new file mode 100644 index 00000000000000..9e54a1f0a86864 --- /dev/null +++ b/packages/base-styles/package.json @@ -0,0 +1,25 @@ +{ + "name": "@wordpress/base-styles", + "version": "1.0.0-alpha.1", + "description": "Base SCSS utilities and variables for WordPress.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "sass", + "scss", + "css" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/base-styles/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/base-styles" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/blob/package.json b/packages/blob/package.json index 5d6682a3df85f1..4d04f47eb2a3c7 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blob", - "version": "2.5.0", + "version": "2.5.1", "description": "Blob utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 7c6af26534df97..22ff8e4e8f8c2b 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "1.0.0", + "version": "1.0.3", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -29,6 +29,7 @@ "@wordpress/data": "file:../data", "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", + "@wordpress/plugins": "file:../plugins", "lodash": "^4.17.15" }, "publishConfig": { diff --git a/packages/block-directory/src/index.js b/packages/block-directory/src/index.js index 5c088e8d5928d3..3af7027bf74d02 100644 --- a/packages/block-directory/src/index.js +++ b/packages/block-directory/src/index.js @@ -2,5 +2,4 @@ * Internal dependencies */ import './store'; - -export { default as DownloadableBlocksPanel } from './components/downloadable-blocks-panel'; +import './plugins'; diff --git a/packages/block-directory/src/plugins/index.js b/packages/block-directory/src/plugins/index.js new file mode 100644 index 00000000000000..917b8567c2d87f --- /dev/null +++ b/packages/block-directory/src/plugins/index.js @@ -0,0 +1,15 @@ +/** + * WordPress dependencies + */ +import { registerPlugin } from '@wordpress/plugins'; + +/** + * Internal dependencies + */ +import InserterMenuDownloadableBlocksPanel from './inserter-menu-downloadable-blocks-panel'; + +registerPlugin( 'block-directory', { + render() { + return ; + }, +} ); diff --git a/packages/editor/src/components/inserter-menu-downloadable-blocks-panel/index.js b/packages/block-directory/src/plugins/inserter-menu-downloadable-blocks-panel/index.js similarity index 89% rename from packages/editor/src/components/inserter-menu-downloadable-blocks-panel/index.js rename to packages/block-directory/src/plugins/inserter-menu-downloadable-blocks-panel/index.js index d19d76ba42ad84..9fb2cc8105dc0b 100644 --- a/packages/editor/src/components/inserter-menu-downloadable-blocks-panel/index.js +++ b/packages/block-directory/src/plugins/inserter-menu-downloadable-blocks-panel/index.js @@ -7,9 +7,13 @@ import { debounce } from 'lodash'; * WordPress dependencies */ import { __experimentalInserterMenuExtension } from '@wordpress/block-editor'; -import { DownloadableBlocksPanel } from '@wordpress/block-directory'; import { useState } from '@wordpress/element'; +/** + * Internal dependencies + */ +import DownloadableBlocksPanel from '../../components/downloadable-blocks-panel'; + function InserterMenuDownloadableBlocksPanel() { const [ debouncedFilterValue, setFilterValue ] = useState( '' ); diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index d9c7818065c0b5..432d36a72ac70c 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -82,6 +82,14 @@ _Related_ Undocumented declaration. +# **BlockBreadcrumb** + +Block breadcrumb component, displaying the hierarchy of the current block selection as a breadcrumb. + +_Returns_ + +- `WPElement`: Block Breadcrumb. + # **BlockControls** Undocumented declaration. @@ -137,7 +145,7 @@ _Parameters_ _Returns_ -- `WPElement`: Rendered element. +- `WPComponent`: The component to be rendered. # **BlockSelectionClearer** @@ -171,6 +179,10 @@ _Related_ Undocumented declaration. +# **ColorPaletteControl** + +Undocumented declaration. + # **ContrastChecker** Undocumented declaration. @@ -392,6 +404,7 @@ The default editor settings **experimentalEnableLegacyWidgetBlock boolean Whether the user has enabled the Legacy Widget Block **experimentalEnableMenuBlock boolean Whether the user has enabled the Menu Block **experimentalBlockDirectory boolean Whether the user has enabled the Block Directory + \_\_experimentalEnableFullSiteEditing boolean Whether the user has enabled Full Site Editing # **SkipToSelectedBlock** @@ -446,6 +459,10 @@ _Related_ - +# **useBlockEditContext** + +Undocumented declaration. + # **Warning** Undocumented declaration. diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 8517b2126deadd..9d9f00f675fb27 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-editor", - "version": "3.2.0", + "version": "3.2.3", "description": "Generic block editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-editor/src/components/autocomplete/index.native.js b/packages/block-editor/src/components/autocomplete/index.native.js new file mode 100644 index 00000000000000..461f67a0a4bcbe --- /dev/null +++ b/packages/block-editor/src/components/autocomplete/index.native.js @@ -0,0 +1 @@ +export default () => null; diff --git a/packages/block-editor/src/components/block-breadcrumb/index.js b/packages/block-editor/src/components/block-breadcrumb/index.js new file mode 100644 index 00000000000000..adc96ba4eb2d51 --- /dev/null +++ b/packages/block-editor/src/components/block-breadcrumb/index.js @@ -0,0 +1,77 @@ +/** + * WordPress dependencies + */ +import { Button } from '@wordpress/components'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import BlockTitle from '../block-title'; + +/** + * Block breadcrumb component, displaying the hierarchy of the current block selection as a breadcrumb. + * + * @return {WPElement} Block Breadcrumb. + */ +const BlockBreadcrumb = function() { + const { selectBlock, clearSelectedBlock } = useDispatch( 'core/block-editor' ); + const { clientId, parents, hasSelection } = useSelect( ( select ) => { + const { + getSelectionStart, + getSelectedBlockClientId, + getBlockParents, + } = select( 'core/block-editor' ); + const selectedBlockClientId = getSelectedBlockClientId(); + return { + parents: getBlockParents( selectedBlockClientId ), + clientId: selectedBlockClientId, + hasSelection: !! getSelectionStart().clientId, + }; + }, [] ); + + /* + * Disable reason: The `list` ARIA role is redundant but + * Safari+VoiceOver won't announce the list otherwise. + */ + /* eslint-disable jsx-a11y/no-redundant-roles */ + return ( +
    +
  • + { hasSelection && ( + + ) } + { ! hasSelection && __( 'Document' ) } +
  • + { parents.map( ( parentClientId ) => ( +
  • + +
  • + ) ) } + { !! clientId && ( +
  • + +
  • + ) } +
+ /* eslint-enable jsx-a11y/no-redundant-roles */ + ); +}; + +export default BlockBreadcrumb; diff --git a/packages/block-editor/src/components/block-breadcrumb/style.scss b/packages/block-editor/src/components/block-breadcrumb/style.scss new file mode 100644 index 00000000000000..b20cdd273b2566 --- /dev/null +++ b/packages/block-editor/src/components/block-breadcrumb/style.scss @@ -0,0 +1,41 @@ +.block-editor-block-breadcrumb { + list-style: none; + padding: 0; + margin: 0; + + li { + display: inline-block; + margin: 0; + + &:not(:last-child)::after { + content: "\2192"; // This becomes →. + } + } +} + +.block-editor-block-breadcrumb__button.components-button { + height: $icon-button-size-small; + line-height: $icon-button-size-small; + padding: 0; + + &:hover { + text-decoration: underline; + } + + &:focus { + @include square-style__focus(); + outline-offset: -2px; + box-shadow: none; + } +} + +.block-editor-block-breadcrumb__current { + cursor: default; +} + +.block-editor-block-breadcrumb__button.components-button, +.block-editor-block-breadcrumb__current { + color: $dark-gray-500; + padding: 0 $grid-size; + font-size: inherit; +} diff --git a/packages/block-editor/src/components/block-edit/context.js b/packages/block-editor/src/components/block-edit/context.js index 863cdc3e5d6fa5..08f2ac9f1dd6b1 100644 --- a/packages/block-editor/src/components/block-edit/context.js +++ b/packages/block-editor/src/components/block-edit/context.js @@ -6,19 +6,29 @@ import { noop } from 'lodash'; /** * WordPress dependencies */ -import { createContext } from '@wordpress/element'; +import { createContext, useContext } from '@wordpress/element'; import { createHigherOrderComponent } from '@wordpress/compose'; -const { Consumer, Provider } = createContext( { +const Context = createContext( { name: '', isSelected: false, focusedElement: null, setFocusedElement: noop, clientId: null, } ); +const { Provider, Consumer } = Context; export { Provider as BlockEditContextProvider }; +/** + * A hook that returns the block edit context. + * + * @return {Object} Block edit context + */ +export function useBlockEditContext() { + return useContext( Context ); +} + /** * A Higher Order Component used to inject BlockEdit context to the * wrapped component. @@ -27,7 +37,7 @@ export { Provider as BlockEditContextProvider }; * expected to return object of props to * merge with the component's own props. * - * @return {Component} Enhanced component with injected context as props. + * @return {WPComponent} Enhanced component with injected context as props. */ export const withBlockEditContext = ( mapContextToProps ) => createHigherOrderComponent( ( OriginalComponent ) => { return ( props ) => ( @@ -46,9 +56,9 @@ export const withBlockEditContext = ( mapContextToProps ) => createHigherOrderCo * A Higher Order Component used to render conditionally the wrapped * component only when the BlockEdit has selected state set. * - * @param {Component} OriginalComponent Component to wrap. + * @param {WPComponent} OriginalComponent Component to wrap. * - * @return {Component} Component which renders only when the BlockEdit is selected. + * @return {WPComponent} Component which renders only when the BlockEdit is selected. */ export const ifBlockEditSelected = createHigherOrderComponent( ( OriginalComponent ) => { return ( props ) => ( diff --git a/packages/block-editor/src/components/block-edit/index.js b/packages/block-editor/src/components/block-edit/index.js index 63c475a50692ff..403a5cd87898e3 100644 --- a/packages/block-editor/src/components/block-edit/index.js +++ b/packages/block-editor/src/components/block-edit/index.js @@ -12,7 +12,7 @@ import { Component } from '@wordpress/element'; * Internal dependencies */ import Edit from './edit'; -import { BlockEditContextProvider } from './context'; +import { BlockEditContextProvider, useBlockEditContext } from './context'; class BlockEdit extends Component { constructor() { @@ -44,3 +44,4 @@ class BlockEdit extends Component { } export default BlockEdit; +export { useBlockEditContext }; diff --git a/packages/block-editor/src/components/block-list/block-async-mode-provider.js b/packages/block-editor/src/components/block-list/block-async-mode-provider.js index aaa2e709db92c6..3cdde80050761c 100644 --- a/packages/block-editor/src/components/block-list/block-async-mode-provider.js +++ b/packages/block-editor/src/components/block-list/block-async-mode-provider.js @@ -1,10 +1,7 @@ /** * WordPress dependencies */ -import { - __experimentalAsyncModeProvider as AsyncModeProvider, - useSelect, -} from '@wordpress/data'; +import { AsyncModeProvider, useSelect } from '@wordpress/data'; const BlockAsyncModeProvider = ( { children, clientId, isBlockInSelection } ) => { const isParentOfSelectedBlock = useSelect( ( select ) => { diff --git a/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.js b/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.js index cdd54ffdcaca1b..7e09fe8de46f79 100644 --- a/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.js +++ b/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.js @@ -10,21 +10,23 @@ import styles from './block-mobile-floating-toolbar.scss'; /** * WordPress dependencies */ +import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { createSlotFill } from '@wordpress/components'; const { Fill, Slot } = createSlotFill( 'FloatingToolbar' ); -function FloatingToolbar( { children } ) { +const FloatingToolbarFill = ( { children, getStylesFromColorScheme } ) => { return ( { ( { innerFloatingToolbar } ) => { + const fillStyle = getStylesFromColorScheme( styles.floatingToolbarFillColor, styles.floatingToolbarFillColorDark ); return ( { children } @@ -33,8 +35,9 @@ function FloatingToolbar( { children } ) { ); -} +}; +const FloatingToolbar = compose( withPreferredColorScheme )( FloatingToolbarFill ); FloatingToolbar.Slot = Slot; export default FloatingToolbar; diff --git a/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.scss b/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.scss index 713633516fca23..a9fbd1243e89ab 100644 --- a/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.scss +++ b/packages/block-editor/src/components/block-list/block-mobile-floating-toolbar.native.scss @@ -1,5 +1,4 @@ .floatingToolbarFill { - background-color: $dark-gray-500; margin: auto; min-width: 100; max-height: $floating-toolbar-height; @@ -13,3 +12,11 @@ justify-content: center; align-self: center; } + +.floatingToolbarFillColor { + background-color: rgba(#1d2327, 0.85); +} + +.floatingToolbarFillColorDark { + background-color: rgba(#3c434a, 0.85); +} diff --git a/packages/block-editor/src/components/block-list/block-mobile-toolbar.js b/packages/block-editor/src/components/block-list/block-mobile-toolbar.js index f35a7add73641a..8af64ec12c021b 100644 --- a/packages/block-editor/src/components/block-list/block-mobile-toolbar.js +++ b/packages/block-editor/src/components/block-list/block-mobile-toolbar.js @@ -9,11 +9,11 @@ import { ifViewportMatches } from '@wordpress/viewport'; import BlockMover from '../block-mover'; import VisualEditorInserter from '../inserter'; -function BlockMobileToolbar( { clientId } ) { +function BlockMobileToolbar( { clientId, moverDirection } ) { return (
- +
); } diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 777cb54933a80b..48b5506e452f2f 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -67,6 +67,7 @@ function BlockListBlock( { mode, isFocusMode, hasFixedToolbar, + moverDirection, isLocked, clientId, rootClientId, @@ -450,6 +451,20 @@ function BlockListBlock( { }; } const blockElementId = `block-${ clientId }`; + const blockMover = ( + + ); // We wrap the BlockEdit component in a div that hides it when editing in // HTML mode. This allows us to render all of the ancillary pieces @@ -511,22 +526,18 @@ function BlockListBlock( { rootClientId={ rootClientId } /> { isFirstMultiSelected && ( - + ) } -
- { shouldRenderMovers && ( - +
+ { shouldRenderMovers && ( moverDirection === 'vertical' ) && blockMover } { shouldShowBreadcrumb && ( ) } + { shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover } { ! isValid && [ { !! hasError && } { shouldShowMobileToolbar && ( - + ) }
diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index 456c5fad6ac2ab..70d8ff2435b907 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -25,6 +25,7 @@ import BlockEdit from '../block-edit'; import BlockInvalidWarning from './block-invalid-warning'; import BlockMobileToolbar from './block-mobile-toolbar'; import FloatingToolbar from './block-mobile-floating-toolbar'; +import Breadcrumbs from './breadcrumb'; import NavigateUpSVG from './nav-up-icon'; class BlockListBlock extends Component { @@ -136,6 +137,7 @@ class BlockListBlock extends Component { /> + ) } { const { setNavigationMode } = useDispatch( 'core/block-editor' ); diff --git a/packages/block-editor/src/components/block-list/breadcrumb.native.js b/packages/block-editor/src/components/block-list/breadcrumb.native.js new file mode 100644 index 00000000000000..0bea56e4da6ddf --- /dev/null +++ b/packages/block-editor/src/components/block-list/breadcrumb.native.js @@ -0,0 +1,68 @@ +/** + * WordPress dependencies + */ +import { Icon } from '@wordpress/components'; +import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; +import { getBlockType } from '@wordpress/blocks'; + +/** + * External dependencies + */ +import { View, Text, TouchableOpacity } from 'react-native'; + +/** + * Internal dependencies + */ +import BlockTitle from '../block-title'; +import SubdirectorSVG from './subdirectory-icon'; + +import styles from './breadcrumb.scss'; + +const BlockBreadcrumb = ( { clientId, blockIcon, rootClientId, rootBlockIcon } ) => { + return ( + + {/* Open BottomSheet with markup */} }> + { rootClientId && rootBlockIcon && ( + [ , + , + ] + ) } + + + + + ); +}; + +export default compose( [ + withSelect( ( select, { clientId } ) => { + const { + getBlockRootClientId, + getBlockName, + } = select( 'core/block-editor' ); + + const blockName = getBlockName( clientId ); + const blockType = getBlockType( blockName ); + const blockIcon = blockType.icon; + + const rootClientId = getBlockRootClientId( clientId ); + + if ( ! rootClientId ) { + return { + clientId, + blockIcon, + }; + } + const rootBlockName = getBlockName( rootClientId ); + const rootBlockType = getBlockType( rootBlockName ); + const rootBlockIcon = rootBlockType.icon; + + return { + clientId, + blockIcon, + rootClientId, + rootBlockIcon, + }; + } ), +] )( BlockBreadcrumb ); diff --git a/packages/block-editor/src/components/block-list/breadcrumb.native.scss b/packages/block-editor/src/components/block-list/breadcrumb.native.scss new file mode 100644 index 00000000000000..8e9b103446291c --- /dev/null +++ b/packages/block-editor/src/components/block-list/breadcrumb.native.scss @@ -0,0 +1,28 @@ +.breadcrumbContainer { + flex-direction: row; + align-items: center; + justify-content: flex-start; + padding-left: 5; + padding-right: 15; +} + +.breadcrumbTitle { + color: $white; + margin-left: 4; +} + +.icon { + color: $white; +} + +.button { + flex-direction: row; + align-items: center; +} + +.arrow { + color: $light-opacity-light-700; + margin-top: -4px; + margin-left: 4; + margin-right: 4; +} diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index 25386d5039c12c..54511ac941ecc4 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -17,7 +17,7 @@ import { Component } from '@wordpress/element'; import { withSelect, withDispatch, - __experimentalAsyncModeProvider as AsyncModeProvider, + AsyncModeProvider, } from '@wordpress/data'; import { compose } from '@wordpress/compose'; @@ -195,6 +195,7 @@ class BlockList extends Component { className, blockClientIds, rootClientId, + __experimentalMoverDirection: moverDirection = 'vertical', isDraggable, selectedBlockClientId, multiSelectedBlockClientIds, @@ -227,6 +228,7 @@ class BlockList extends Component { blockRef={ this.setBlockRef } onSelectionStart={ this.onSelectionStart } isDraggable={ isDraggable } + moverDirection={ moverDirection } // This prop is explicitely computed and passed down // to avoid being impacted by the async mode diff --git a/packages/block-editor/src/components/block-list/multi-controls.js b/packages/block-editor/src/components/block-list/multi-controls.js index e4dd88aeda030f..2f49ca2ca0a3b4 100644 --- a/packages/block-editor/src/components/block-list/multi-controls.js +++ b/packages/block-editor/src/components/block-list/multi-controls.js @@ -11,6 +11,7 @@ import BlockMover from '../block-mover'; function BlockListMultiControls( { multiSelectedBlockClientIds, isSelecting, + moverDirection, } ) { if ( isSelecting ) { return null; @@ -19,6 +20,7 @@ function BlockListMultiControls( { return ( ); } diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index 761632c49d7c7f..e06667a0f914e7 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -101,6 +101,9 @@ .block-editor-block-list__block-edit { position: relative; + &.has-mover-inside > [data-block] { + display: flex; + } &::before { z-index: z-index(".block-editor-block-list__block-edit::before"); diff --git a/packages/block-editor/src/components/block-list/subdirectory-icon.js b/packages/block-editor/src/components/block-list/subdirectory-icon.js new file mode 100644 index 00000000000000..9da2bc4a1c88a9 --- /dev/null +++ b/packages/block-editor/src/components/block-list/subdirectory-icon.js @@ -0,0 +1,18 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/components'; + +const Subdirectory = ( { ...extraProps } ) => ( + + + ) +; + +export default Subdirectory; diff --git a/packages/block-editor/src/components/block-mover/icons.js b/packages/block-editor/src/components/block-mover/icons.js index 06bf5601f439db..2a15b8a6159d5d 100644 --- a/packages/block-editor/src/components/block-mover/icons.js +++ b/packages/block-editor/src/components/block-mover/icons.js @@ -9,12 +9,24 @@ export const upArrow = ( ); +export const leftArrow = ( + + + +); + export const downArrow = ( ); +export const rightArrow = ( + + + +); + export const dragHandle = ( @@ -107,6 +141,8 @@ export class BlockMover extends Component { isFirst, isLast, 1, + orientation, + isRTL, ) } @@ -125,12 +161,17 @@ export default compose( const blockOrder = getBlockOrder( rootClientId ); const firstIndex = getBlockIndex( firstClientId, rootClientId ); const lastIndex = getBlockIndex( last( normalizedClientIds ), rootClientId ); + const { getSettings } = select( 'core/block-editor' ); + const { + isRTL, + } = getSettings(); return { blockType: block ? getBlockType( block.name ) : null, isLocked: getTemplateLock( rootClientId ) === 'all', rootClientId, firstIndex, + isRTL, isFirst: firstIndex === 0, isLast: lastIndex === blockOrder.length - 1, }; diff --git a/packages/block-editor/src/components/block-mover/mover-description.js b/packages/block-editor/src/components/block-mover/mover-description.js index 44174ce85534ca..d9a62de176d821 100644 --- a/packages/block-editor/src/components/block-mover/mover-description.js +++ b/packages/block-editor/src/components/block-mover/mover-description.js @@ -14,12 +14,30 @@ import { __, _n, sprintf } from '@wordpress/i18n'; * @param {boolean} isLast This is the last block. * @param {number} dir Direction of movement (> 0 is considered to be going * down, < 0 is up). + * @param {string} orientation The orientation of the block movers, vertical or + * horizontal. + * @param {boolean} isRTL True if current writing system is right to left. * * @return {string} Label for the block movement controls. */ -export function getBlockMoverDescription( selectedCount, type, firstIndex, isFirst, isLast, dir ) { +export function getBlockMoverDescription( selectedCount, type, firstIndex, isFirst, isLast, dir, orientation, isRTL ) { const position = ( firstIndex + 1 ); + const getMovementDirection = ( moveDirection ) => { + if ( moveDirection === 'up' ) { + if ( orientation === 'horizontal' ) { + return isRTL ? 'right' : 'left'; + } + return 'up'; + } else if ( moveDirection === 'down' ) { + if ( orientation === 'horizontal' ) { + return isRTL ? 'left' : 'right'; + } + return 'down'; + } + return null; + }; + if ( selectedCount > 1 ) { return getMultiBlockMoverDescription( selectedCount, firstIndex, isFirst, isLast, dir ); } @@ -32,35 +50,46 @@ export function getBlockMoverDescription( selectedCount, type, firstIndex, isFir if ( dir > 0 && ! isLast ) { // moving down return sprintf( - // translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: New position - __( 'Move %1$s block from position %2$d down to position %3$d' ), + // translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: Direction of movement ( up, down, left, right ), 4: New position + __( 'Move %1$s block from position %2$d %3$s to position %4$d' ), type, position, - ( position + 1 ) + getMovementDirection( 'down' ), + ( position + 1 ), ); } if ( dir > 0 && isLast ) { // moving down, and is the last item - // translators: %s: Type of block (i.e. Text, Image etc) - return sprintf( __( 'Block %s is at the end of the content and can’t be moved down' ), type ); + // translators: 1: Type of block (i.e. Text, Image etc), 2: Direction of movement ( up, down, left, right ) + return sprintf( + __( 'Block %1$s is at the end of the content and can’t be moved %2$s' ), + type, + getMovementDirection( 'down' ), + + ); } if ( dir < 0 && ! isFirst ) { // moving up return sprintf( - // translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: New position - __( 'Move %1$s block from position %2$d up to position %3$d' ), + // translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: Direction of movement ( up, down, left, right ), 4: New position + __( 'Move %1$s block from position %2$d %3$s to position %4$d' ), type, position, - ( position - 1 ) + getMovementDirection( 'up' ), + ( position - 1 ), ); } if ( dir < 0 && isFirst ) { // moving up, and is the first item - // translators: %s: Type of block (i.e. Text, Image etc) - return sprintf( __( 'Block %s is at the beginning of the content and can’t be moved up' ), type ); + // translators: 1: Type of block (i.e. Text, Image etc), 2: Direction of movement ( up, down, left, right ) + return sprintf( + __( 'Block %1$s is at the beginning of the content and can’t be moved %2$s' ), + type, + getMovementDirection( 'up' ), + ); } } diff --git a/packages/block-editor/src/components/block-mover/style.scss b/packages/block-editor/src/components/block-mover/style.scss index a506b3282fad42..260d456c73d3c0 100644 --- a/packages/block-editor/src/components/block-mover/style.scss +++ b/packages/block-editor/src/components/block-mover/style.scss @@ -1,5 +1,4 @@ .block-editor-block-mover { - @include break-small() { min-height: $empty-paragraph-height; opacity: 0; @@ -20,13 +19,38 @@ // 24px is the smallest size of a good pressable button. // With 3 pieces of side UI, that comes to a total of 72px. // To vertically center against a 56px paragraph, move upwards 72px - 56px / 2. - // Don't do this for wide, fullwide, or mobile. - .block-editor-block-list__block:not([data-align="wide"]):not([data-align="full"]) & { - margin-top: -$grid-size; + margin-top: -$grid-size; + } + + &.is-horizontal { + margin-top: 5px; // The height of the appender is 36px. This pushes down the mover to be centered according to that. + margin-right: $grid-size; + padding-right: 0; + min-height: auto; + width: ($icon-button-size-small * 2) + ($border-width * 2); + height: $icon-button-size-small + ($border-width * 2); + display: flex; + + .block-editor-block-mover__control { + width: $icon-button-size-small; + height: $icon-button-size-small; + + svg { + width: $icon-button-size-small; + padding: 3px; + } } } } +// Don't add negative vertical margin for wide, fullwide, or mobile. +// @todo: simplify this selector. +@include break-small() { + .block-editor-block-list__block:not([data-align="wide"]):not([data-align="full"]) .editor-block-mover:not(.is-horizontal) { + margin-top: 0; + } +} + // Mover icon buttons. .block-editor-block-mover__control { display: flex; diff --git a/packages/block-editor/src/components/block-navigation/list.js b/packages/block-editor/src/components/block-navigation/list.js index 0a34d24ff4e4b6..a0910caf5b41cc 100644 --- a/packages/block-editor/src/components/block-navigation/list.js +++ b/packages/block-editor/src/components/block-navigation/list.js @@ -10,12 +10,35 @@ import classnames from 'classnames'; import { Button } from '@wordpress/components'; import { getBlockType } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; +import { create, getTextContent } from '@wordpress/rich-text'; /** * Internal dependencies */ import BlockIcon from '../block-icon'; +/** + * Get the block display name, if it has one, or the block title if it doesn't. + * + * @param {Object} blockType The block type. + * @param {Object} attributes The values of the block's attributes + * + * @return {string} The display name value. + */ +function getBlockDisplayName( blockType, attributes ) { + const displayNameAttribute = blockType.__experimentalDisplayName; + + if ( ! displayNameAttribute || ! attributes[ displayNameAttribute ] ) { + return blockType.title; + } + + // Strip any formatting. + const richTextValue = create( { html: attributes[ displayNameAttribute ] } ); + const formatlessDisplayName = getTextContent( richTextValue ); + + return formatlessDisplayName; +} + export default function BlockNavigationList( { blocks, selectedBlockClientId, @@ -43,7 +66,7 @@ export default function BlockNavigationList( { onClick={ () => selectBlock( block.clientId ) } > - { blockType.title } + { getBlockDisplayName( blockType, block.attributes ) } { isSelected && { __( '(selected block)' ) } }
diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js index 8a6692a422a6f7..00ae9233a12501 100644 --- a/packages/block-editor/src/components/block-preview/index.js +++ b/packages/block-editor/src/components/block-preview/index.js @@ -137,7 +137,7 @@ export function BlockPreview( { blocks, viewportWidth = 700, padding, settings } * * @param {Array|Object} blocks A block instance (object) or an array of blocks to be previewed. * @param {number} viewportWidth Width of the preview container in pixels. Controls at what size the blocks will be rendered inside the preview. Default: 700. - * @return {WPElement} Rendered element. + * @return {WPComponent} The component to be rendered. */ export default withSelect( ( select ) => { return { diff --git a/packages/block-editor/src/components/block-settings/container.native.js b/packages/block-editor/src/components/block-settings/container.native.js index c51a42b39de6d0..1b59377f29161c 100644 --- a/packages/block-editor/src/components/block-settings/container.native.js +++ b/packages/block-editor/src/components/block-settings/container.native.js @@ -6,12 +6,18 @@ import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { InspectorControls } from '@wordpress/block-editor'; +/** + * Internal dependencies + */ +import styles from './container.native.scss'; + function BottomSheetSettings( { editorSidebarOpened, closeGeneralSidebar, ...props } ) { return ( diff --git a/packages/block-editor/src/components/block-settings/container.native.scss b/packages/block-editor/src/components/block-settings/container.native.scss new file mode 100644 index 00000000000000..2eed1c9e3f2d00 --- /dev/null +++ b/packages/block-editor/src/components/block-settings/container.native.scss @@ -0,0 +1,4 @@ +.content { + padding-left: 0; + padding-right: 0; +} diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index e83f34bc54eadd..a6247b03d3177b 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -49,6 +49,8 @@ .block-editor-block-toolbar__slot { // Required for IE11. display: inline-block; + // Fix for toolbar button misalignment on IE11 + line-height: 0; // IE11 doesn't read rules inside this query. They are applied only to modern browsers. @supports (position: sticky) { diff --git a/packages/block-editor/src/components/button-block-appender/index.js b/packages/block-editor/src/components/button-block-appender/index.js index 7fd751165f5b45..1cbbb5980903a2 100644 --- a/packages/block-editor/src/components/button-block-appender/index.js +++ b/packages/block-editor/src/components/button-block-appender/index.js @@ -6,8 +6,8 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { Button, Icon } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { Button, Icon, Tooltip } from '@wordpress/components'; +import { _x, sprintf } from '@wordpress/i18n'; /** * Internal dependencies @@ -21,17 +21,31 @@ function ButtonBlockAppender( { rootClientId, className } ) { ( - - ) } + renderToggle={ ( { onToggle, disabled, isOpen, blockTitle, hasSingleBlockType } ) => { + let label; + if ( hasSingleBlockType ) { + // translators: %s: the name of the block when there is only one + label = sprintf( _x( 'Add %s', 'directly add the only allowed block' ), blockTitle ); + } else { + label = _x( 'Add block', 'Generic label for block inserter button' ); + } + const isToggleButton = ! hasSingleBlockType; + return ( + + + + ); + } } isAppender /> diff --git a/packages/block-editor/src/components/colors/index.js b/packages/block-editor/src/components/colors/index.js index f6b6fac984db89..cadb34a8c9aa37 100644 --- a/packages/block-editor/src/components/colors/index.js +++ b/packages/block-editor/src/components/colors/index.js @@ -7,3 +7,4 @@ export { createCustomColorsHOC, default as withColors, } from './with-colors'; +export { default as __experimentalUseColors } from './use-colors'; diff --git a/packages/block-editor/src/components/colors/use-colors.js b/packages/block-editor/src/components/colors/use-colors.js new file mode 100644 index 00000000000000..d69ee1bdbadf9a --- /dev/null +++ b/packages/block-editor/src/components/colors/use-colors.js @@ -0,0 +1,226 @@ +/** + * External dependencies + */ +import memoize from 'memize'; +import classnames from 'classnames'; +import { + camelCase, + kebabCase, + map, + startCase, +} from 'lodash'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { + useCallback, + useMemo, + Children, + cloneElement, +} from '@wordpress/element'; + +/** + * Internal dependencies + */ +import PanelColorSettings from '../panel-color-settings'; +import ContrastChecker from '../contrast-checker'; +import InspectorControls from '../inspector-controls'; +import { useBlockEditContext } from '../block-edit'; + +const ColorPanel = ( { + title, + colorSettings, + colorPanelProps, + contrastCheckerProps, + components, + panelChildren, +} ) => ( + + { contrastCheckerProps && + map( components, ( ( Component, key ) => ( + + ) ) ) } + { typeof panelChildren === 'function' ? + panelChildren( components ) : + panelChildren } + +); +const InspectorControlsColorPanel = ( props ) => ( + + + +); + +export default function __experimentalUseColors( + colorConfigs, + { + panelTitle = __( 'Color Settings' ), + colorPanelProps, + contrastCheckerProps, + panelChildren, + } = { + panelTitle: __( 'Color Settings' ), + }, + deps = [] +) { + const { clientId } = useBlockEditContext(); + const { attributes, settingsColors } = useSelect( + ( select ) => { + const { getBlockAttributes, getSettings } = select( 'core/block-editor' ); + return { + attributes: getBlockAttributes( clientId ), + settingsColors: getSettings().colors, + }; + }, + [ clientId ] + ); + const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); + const setAttributes = useCallback( + ( newAttributes ) => updateBlockAttributes( clientId, newAttributes ), + [ updateBlockAttributes, clientId ] + ); + + const createComponent = useMemo( + () => + memoize( + ( name, property, className, color, colorValue, customColor ) => ( { + children, + className: componentClassName = '', + style: componentStyle = {}, + } ) => + // Clone children, setting the style property from the color configuration, + // if not already set explicitly through props. + Children.map( children, ( child ) => { + let colorStyle = {}; + if ( color ) { + colorStyle = { [ property ]: colorValue }; + } else if ( customColor ) { + colorStyle = { [ property ]: customColor }; + } + + return cloneElement( child, { + className: classnames( + componentClassName, + child.props.className, + { + [ `has-${ kebabCase( color ) }-${ kebabCase( property ) }` ]: color, + [ className || `has-${ kebabCase( name ) }` ]: color || customColor, + } + ), + style: { + ...colorStyle, + ...componentStyle, + ...( child.props.style || {} ), + }, + } ); + } ), + { maxSize: colorConfigs.length } + ), + [ colorConfigs.length ] + ); + const createSetColor = useMemo( + () => + memoize( + ( name, colors ) => ( newColor ) => { + const color = colors.find( ( _color ) => _color.color === newColor ); + setAttributes( { + [ color ? camelCase( `custom ${ name }` ) : name ]: undefined, + } ); + setAttributes( { + [ color ? name : camelCase( `custom ${ name }` ) ]: color ? + color.slug : + newColor, + } ); + }, + { + maxSize: colorConfigs.length, + } + ), + [ setAttributes, colorConfigs.length ] + ); + + return useMemo( () => { + const colorSettings = {}; + + const components = colorConfigs.reduce( ( acc, colorConfig ) => { + if ( typeof colorConfig === 'string' ) { + colorConfig = { name: colorConfig }; + } + const { + name, // E.g. 'backgroundColor'. + property = name, // E.g. 'backgroundColor'. + className, + + panelLabel = startCase( name ), // E.g. 'Background Color'. + componentName = panelLabel.replace( /\s/g, '' ), // E.g. 'BackgroundColor'. + + color = colorConfig.color, + colors = settingsColors, + } = { + ...colorConfig, + color: attributes[ colorConfig.name ], + }; + + // We memoize the non-primitives to avoid unnecessary updates + // when they are used as props for other components. + const _color = colors.find( ( __color ) => __color.slug === color ); + acc[ componentName ] = createComponent( + name, + property, + className, + color, + _color && _color.color, + attributes[ camelCase( `custom ${ name }` ) ] + ); + acc[ componentName ].displayName = componentName; + acc[ componentName ].color = color; + acc[ componentName ].setColor = createSetColor( name, colors ); + + colorSettings[ componentName ] = { + value: _color ? + _color.color : + attributes[ camelCase( `custom ${ name }` ) ], + onChange: acc[ componentName ].setColor, + label: panelLabel, + colors, + }; + // These settings will be spread over the `colors` in + // `colorPanelProps`, so we need to unset the key here, + // if not set to an actual value, to avoid overwriting + // an actual value in `colorPanelProps`. + if ( ! colors ) { + delete colorSettings[ componentName ].colors; + } + + return acc; + }, {} ); + + const wrappedColorPanelProps = { + title: panelTitle, + colorSettings, + colorPanelProps, + contrastCheckerProps, + components, + panelChildren, + }; + return { + ...components, + ColorPanel: , + InspectorControlsColorPanel: ( + + ), + }; + }, [ attributes, setAttributes, ...deps ] ); +} diff --git a/packages/block-editor/src/components/colors/with-colors.js b/packages/block-editor/src/components/colors/with-colors.js index b6f33b1b8590f7..4544c6c8c49e1e 100644 --- a/packages/block-editor/src/components/colors/with-colors.js +++ b/packages/block-editor/src/components/colors/with-colors.js @@ -49,7 +49,7 @@ const withEditorColorPalette = () => withSelect( ( select ) => { * @param {Array} colorTypes An array of color types (e.g. 'backgroundColor, borderColor). * @param {Function} withColorPalette A HOC for injecting the 'colors' prop into the WrappedComponent. * - * @return {Component} The component that can be used as a HOC. + * @return {WPComponent} The component that can be used as a HOC. */ function createColorHOC( colorTypes, withColorPalette ) { const colorMap = reduce( colorTypes, ( colorObject, colorType ) => { diff --git a/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap index 9a09dae0d47674..08a0f7df9efe74 100644 --- a/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/default-block-appender/test/__snapshots__/index.js.snap @@ -29,7 +29,7 @@ exports[`DefaultBlockAppender should append a default block when input focused 1 rows={1} value="Start writing or type / to choose a block" /> - @@ -53,7 +53,7 @@ exports[`DefaultBlockAppender should match snapshot 1`] = ` rows={1} value="Start writing or type / to choose a block" /> - @@ -77,7 +77,7 @@ exports[`DefaultBlockAppender should optionally show without prompt 1`] = ` rows={1} value="" /> - diff --git a/packages/block-editor/src/components/gradient-picker/control.js b/packages/block-editor/src/components/gradient-picker/control.js index 73609ffdf17e27..a0eeecc6facc25 100644 --- a/packages/block-editor/src/components/gradient-picker/control.js +++ b/packages/block-editor/src/components/gradient-picker/control.js @@ -3,19 +3,27 @@ * External dependencies */ import classnames from 'classnames'; +import { isEmpty } from 'lodash'; /** * WordPress dependencies */ import { BaseControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import GradientPicker from './'; -export default function( { className, ...props } ) { +export default function( { className, label = __( 'Gradient Presets' ), ...props } ) { + const gradients = useSelect( ( select ) => ( + select( 'core/block-editor' ).getSettings().gradients + ) ); + if ( isEmpty( gradients ) ) { + return null; + } return ( - { __( 'Gradient Presets' ) } + { label } diff --git a/packages/block-editor/src/components/gradient-picker/panel.js b/packages/block-editor/src/components/gradient-picker/panel.js new file mode 100644 index 00000000000000..2be91125cbc110 --- /dev/null +++ b/packages/block-editor/src/components/gradient-picker/panel.js @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import { isEmpty } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { PanelBody } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import GradientPicker from './control'; + +export default function GradientPanel( props ) { + const gradients = useSelect( ( select ) => ( + select( 'core/block-editor' ).getSettings().gradients + ) ); + if ( isEmpty( gradients ) ) { + return null; + } + return ( + + + + ); +} diff --git a/packages/block-editor/src/components/gradients/index.js b/packages/block-editor/src/components/gradients/index.js new file mode 100644 index 00000000000000..b08be314861e03 --- /dev/null +++ b/packages/block-editor/src/components/gradients/index.js @@ -0,0 +1,77 @@ +/** + * External dependencies + */ +import { find } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useCallback } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { useBlockEditContext } from '../block-edit'; + +export function __experimentalGetGradientClass( gradientSlug ) { + if ( ! gradientSlug ) { + return undefined; + } + return `has-${ gradientSlug }-gradient-background`; +} + +function getGradientValueBySlug( gradients, slug ) { + const gradient = find( gradients, [ 'slug', slug ] ); + return gradient && gradient.gradient; +} + +function getGradientSlugByValue( gradients, value ) { + const gradient = find( gradients, [ 'gradient', value ] ); + return gradient && gradient.slug; +} + +export function __experimentalUseGradient( { + gradientAttribute = 'gradient', + customGradientAttribute = 'customGradient', +} = {} ) { + const { clientId } = useBlockEditContext(); + + const { gradients, gradient, customGradient } = useSelect( ( select ) => { + const { getBlockAttributes, getSettings } = select( 'core/block-editor' ); + const attributes = getBlockAttributes( clientId ); + return { + gradient: attributes[ gradientAttribute ], + customGradient: attributes[ customGradientAttribute ], + gradients: getSettings().gradients, + }; + }, [ clientId ] ); + + const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); + const setGradient = useCallback( + ( newGradientValue ) => { + const slug = getGradientSlugByValue( gradients, newGradientValue ); + if ( slug ) { + updateBlockAttributes( clientId, { + [ gradientAttribute ]: slug, + [ customGradientAttribute ]: undefined, + } ); + return; + } + updateBlockAttributes( clientId, { + [ gradientAttribute ]: undefined, + [ customGradientAttribute ]: newGradientValue, + } ); + }, + [ gradients, clientId, updateBlockAttributes ] + ); + + const gradientClass = __experimentalGetGradientClass( gradient ); + let gradientValue; + if ( gradient ) { + gradientValue = getGradientValueBySlug( gradients, gradient ); + } else { + gradientValue = customGradient; + } + return { gradientClass, gradientValue, setGradient }; +} diff --git a/packages/block-editor/src/components/ignore-nested-events/index.js b/packages/block-editor/src/components/ignore-nested-events/index.js index 2592ba3abd0527..92bbe5ed44c639 100644 --- a/packages/block-editor/src/components/ignore-nested-events/index.js +++ b/packages/block-editor/src/components/ignore-nested-events/index.js @@ -20,7 +20,7 @@ import { Component, forwardRef, createElement } from '@wordpress/element'; * element should stop propagation but not invoke a callback handler, since it * would be assumed these are invoked by the child element. * - * @type {Component} + * @type {WPComponent} */ export class IgnoreNestedEvents extends Component { constructor() { diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 6e9bb592378678..9b094396d9d8b0 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -3,12 +3,14 @@ */ export * from './colors'; +export * from './gradients'; export * from './font-sizes'; export { default as AlignmentToolbar } from './alignment-toolbar'; export { default as Autocomplete } from './autocomplete'; export { default as BlockAlignmentToolbar } from './block-alignment-toolbar'; +export { default as BlockBreadcrumb } from './block-breadcrumb'; export { default as BlockControls } from './block-controls'; -export { default as BlockEdit } from './block-edit'; +export { default as BlockEdit, useBlockEditContext } from './block-edit'; export { default as BlockFormatControls } from './block-format-controls'; export { default as BlockIcon } from './block-icon'; export { default as BlockNavigationDropdown } from './block-navigation/dropdown'; @@ -16,12 +18,15 @@ export { default as __experimentalBlockNavigationList } from './block-navigation export { default as BlockVerticalAlignmentToolbar } from './block-vertical-alignment-toolbar'; export { default as ButtonBlockerAppender } from './button-block-appender'; export { default as ColorPalette } from './color-palette'; +export { default as ColorPaletteControl } from './color-palette/control'; export { default as ContrastChecker } from './contrast-checker'; export { default as __experimentalGradientPicker } from './gradient-picker'; export { default as __experimentalGradientPickerControl } from './gradient-picker/control'; +export { default as __experimentalGradientPickerPanel } from './gradient-picker/panel'; export { default as InnerBlocks } from './inner-blocks'; export { default as InspectorAdvancedControls } from './inspector-advanced-controls'; export { default as InspectorControls } from './inspector-controls'; +export { default as __experimentalLinkControl } from './link-control'; export { default as MediaPlaceholder } from './media-placeholder'; export { default as MediaUpload } from './media-upload'; export { default as MediaUploadCheck } from './media-upload/check'; diff --git a/packages/block-editor/src/components/index.native.js b/packages/block-editor/src/components/index.native.js index b278e29d4555a9..6baafd5d8f0bbe 100644 --- a/packages/block-editor/src/components/index.native.js +++ b/packages/block-editor/src/components/index.native.js @@ -1,6 +1,7 @@ // Block Creation Components +export { default as BlockAlignmentToolbar } from './block-alignment-toolbar'; export { default as BlockControls } from './block-controls'; -export { default as BlockEdit } from './block-edit'; +export { default as BlockEdit, useBlockEditContext } from './block-edit'; export { default as BlockFormatControls } from './block-format-controls'; export { default as BlockIcon } from './block-icon'; export { default as BlockVerticalAlignmentToolbar } from './block-vertical-alignment-toolbar'; diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 35e1bf10b47ca2..ed147bfe736adf 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -107,6 +107,7 @@ class InnerBlocks extends Component { hasOverlay, renderAppender, template, + __experimentalMoverDirection: moverDirection, __experimentalTemplateOptions: templateOptions, __experimentalOnSelectTemplateOption: onSelectTemplateOption, __experimentalAllowTemplateOptionSkip: allowTemplateOptionSkip, @@ -131,6 +132,7 @@ class InnerBlocks extends Component { ) } diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index 5abb1ae4ef9230..b9fd9dead4b377 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -1,29 +1,46 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, _x, sprintf } from '@wordpress/i18n'; import { Dropdown, IconButton } from '@wordpress/components'; import { Component } from '@wordpress/element'; -import { withSelect } from '@wordpress/data'; +import { withDispatch, withSelect } from '@wordpress/data'; import { compose, ifCondition } from '@wordpress/compose'; +import { + createBlock, + getBlockType, +} from '@wordpress/blocks'; /** * Internal dependencies */ import InserterMenu from './menu'; -const defaultRenderToggle = ( { onToggle, disabled, isOpen } ) => ( - -); +const defaultRenderToggle = ( { onToggle, disabled, isOpen, blockTitle, hasSingleBlockType } ) => { + let label; + if ( hasSingleBlockType ) { + // translators: %s: the name of the block when there is only one + label = sprintf( _x( 'Add %s', 'directly add the only allowed block' ), blockTitle ); + } else { + label = _x( 'Add block', 'Generic label for block inserter button' ); + } + return ( + + ); +}; class Inserter extends Component { constructor() { @@ -56,10 +73,12 @@ class Inserter extends Component { renderToggle( { onToggle, isOpen } ) { const { disabled, + blockTitle, + hasSingleBlockType, renderToggle = defaultRenderToggle, } = this.props; - return renderToggle( { onToggle, isOpen, disabled } ); + return renderToggle( { onToggle, isOpen, disabled, blockTitle, hasSingleBlockType } ); } /** @@ -86,8 +105,10 @@ class Inserter extends Component { } render() { - const { position } = this.props; - + const { position, hasSingleBlockType, insertOnlyAllowedBlock } = this.props; + if ( hasSingleBlockType ) { + return this.renderToggle( { onToggle: insertOnlyAllowedBlock } ); + } return ( { - const { hasInserterItems } = select( 'core/block-editor' ); + const { + hasInserterItems, + __experimentalGetAllowedBlocks, + } = select( 'core/block-editor' ); + + const allowedBlocks = __experimentalGetAllowedBlocks( rootClientId ); + const hasSingleBlockType = allowedBlocks && ( get( allowedBlocks, [ 'length' ], 0 ) === 1 ); + let allowedBlockType = false; + if ( hasSingleBlockType ) { + allowedBlockType = getBlockType( allowedBlocks ); + } return { hasItems: hasInserterItems( rootClientId ), + hasSingleBlockType, + blockTitle: allowedBlockType ? allowedBlockType.title : '', + allowedBlockType, + }; + } ), + withDispatch( ( dispatch, ownProps, { select } ) => { + return { + insertOnlyAllowedBlock() { + const { rootClientId, clientId, isAppender, destinationRootClientId } = ownProps; + const { + hasSingleBlockType, + allowedBlockType, + } = ownProps; + + if ( ! hasSingleBlockType ) { + return; + } + + function getInsertionIndex() { + const { + getBlockIndex, + getBlockSelectionEnd, + getBlockOrder, + } = select( 'core/block-editor' ); + + // If the clientId is defined, we insert at the position of the block. + if ( clientId ) { + return getBlockIndex( clientId, destinationRootClientId ); + } + + // If there a selected block, we insert after the selected block. + const end = getBlockSelectionEnd(); + if ( ! isAppender && end ) { + return getBlockIndex( end, destinationRootClientId ) + 1; + } + + // Otherwise, we insert at the end of the current rootClientId + return getBlockOrder( destinationRootClientId ).length; + } + + const { + insertBlock, + } = dispatch( 'core/block-editor' ); + + const blockToInsert = createBlock( allowedBlockType.name ); + insertBlock( + blockToInsert, + getInsertionIndex(), + rootClientId + ); + }, }; } ), ifCondition( ( { hasItems } ) => hasItems ), diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md new file mode 100644 index 00000000000000..e21c29974301b1 --- /dev/null +++ b/packages/block-editor/src/components/link-control/README.md @@ -0,0 +1,50 @@ +# Link Control + +## Props + +### className + +- Type: `String` +- Required: Yes + +### currentLink + +- Type: `Object` +- Required: Yes + +### currentSettings + +- Type: `Object` +- Required: Yes + +### fetchSearchSuggestions + +- Type: `Function` +- Required: No + +## Event handlers + +### onClose + +- Type: `Function` +- Required: No + +### onKeyDown + +- Type: `Function` +- Required: No + +### onKeyPress + +- Type: `Function` +- Required: No + +### onLinkChange + +- Type: `Function` +- Required: No + +### onSettingChange + +- Type: `Function` +- Required: No diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js new file mode 100644 index 00000000000000..afe72df4f2e93d --- /dev/null +++ b/packages/block-editor/src/components/link-control/index.js @@ -0,0 +1,249 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { isFunction, noop, startsWith } from 'lodash'; + +/** + * WordPress dependencies + */ +import { + Button, + ExternalLink, + Popover, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +import { + useCallback, + useState, + useEffect, + Fragment, +} from '@wordpress/element'; + +import { + safeDecodeURI, + filterURLForDisplay, + isURL, + prependHTTP, + getProtocol, +} from '@wordpress/url'; + +import { withInstanceId, compose } from '@wordpress/compose'; +import { withSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import LinkControlSettingsDrawer from './settings-drawer'; +import LinkControlSearchItem from './search-item'; +import LinkControlSearchInput from './search-input'; + +function LinkControl( { + className, + currentLink, + currentSettings, + fetchSearchSuggestions, + instanceId, + onClose = noop, + onKeyDown = noop, + onKeyPress = noop, + onLinkChange = noop, + onSettingsChange = { noop }, +} ) { + // State + const [ inputValue, setInputValue ] = useState( '' ); + const [ isEditingLink, setIsEditingLink ] = useState( false ); + + // Effects + useEffect( () => { + // If we have a link then stop editing mode + if ( currentLink ) { + setIsEditingLink( false ); + } else { + setIsEditingLink( true ); + } + }, [ currentLink ] ); + + // Handlers + + /** + * onChange LinkControlSearchInput event handler + * + * @param {string} value Current value returned by the search. + */ + const onInputChange = ( value = '' ) => { + setInputValue( value ); + }; + + // Utils + const startEditMode = () => { + if ( isFunction( onLinkChange ) ) { + onLinkChange(); + } + }; + + const closeLinkUI = () => { + resetInput(); + onClose(); + }; + + const resetInput = () => { + setInputValue( '' ); + }; + + const handleDirectEntry = ( value ) => { + let type = 'URL'; + + const protocol = getProtocol( value ) || ''; + + if ( protocol.includes( 'mailto' ) ) { + type = 'mailto'; + } + + if ( protocol.includes( 'tel' ) ) { + type = 'tel'; + } + + if ( startsWith( value, '#' ) ) { + type = 'internal'; + } + + return Promise.resolve( + [ { + id: '-1', + title: value, + url: type === 'URL' ? prependHTTP( value ) : value, + type, + } ] + ); + }; + + const handleEntitySearch = async ( value ) => { + const results = await Promise.all( [ + fetchSearchSuggestions( value ), + handleDirectEntry( value ), + ] ); + + const couldBeURL = ! value.includes( ' ' ); + + // If it's potentially a URL search then concat on a URL search suggestion + // just for good measure. That way once the actual results run out we always + // have a URL option to fallback on. + return couldBeURL ? results[ 0 ].concat( results[ 1 ] ) : results[ 0 ]; + }; + + // Effects + const getSearchHandler = useCallback( ( value ) => { + const protocol = getProtocol( value ) || ''; + const isMailto = protocol.includes( 'mailto' ); + const isInternal = startsWith( value, '#' ); + const isTel = protocol.includes( 'tel' ); + + const handleManualEntry = isInternal || isMailto || isTel || isURL( value ) || ( value && value.includes( 'www.' ) ); + + return ( handleManualEntry ) ? handleDirectEntry( value ) : handleEntitySearch( value ); + }, [ handleDirectEntry, fetchSearchSuggestions ] ); + + // Render Components + const renderSearchResults = ( { suggestionsListProps, buildSuggestionItemProps, suggestions, selectedSuggestion, isLoading } ) => { + const resultsListClasses = classnames( 'block-editor-link-control__search-results', { + 'is-loading': isLoading, + } ); + + const manualLinkEntryTypes = [ 'url', 'mailto', 'tel', 'internal' ]; + + return ( +
+
+ { suggestions.map( ( suggestion, index ) => ( + onLinkChange( suggestion ) } + isSelected={ index === selectedSuggestion } + isURL={ manualLinkEntryTypes.includes( suggestion.type.toLowerCase() ) } + searchTerm={ inputValue } + /> + ) ) } +
+
+ ); + }; + + return ( + +
+
+ + { ( ! isEditingLink && currentLink ) && ( + +

+ { __( 'Currently selected' ) }: +

+
+ + + + { currentLink.title } + + { filterURLForDisplay( safeDecodeURI( currentLink.url ) ) || '' } + + + +
+
+ ) } + + { isEditingLink && ( + + ) } + + { ! isEditingLink && ( + + ) } +
+
+
+ ); +} + +export default compose( + withInstanceId, + withSelect( ( select, ownProps ) => { + if ( ownProps.fetchSearchSuggestions && isFunction( ownProps.fetchSearchSuggestions ) ) { + return; + } + + const { getSettings } = select( 'core/block-editor' ); + return { + fetchSearchSuggestions: getSettings().__experimentalFetchLinkSuggestions, + }; + } ) +)( LinkControl ); diff --git a/packages/block-editor/src/components/link-control/search-input.js b/packages/block-editor/src/components/link-control/search-input.js new file mode 100644 index 00000000000000..84fd5db8359363 --- /dev/null +++ b/packages/block-editor/src/components/link-control/search-input.js @@ -0,0 +1,69 @@ + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { IconButton } from '@wordpress/components'; +import { ENTER } from '@wordpress/keycodes'; + +/** + * Internal dependencies + */ +import { URLInput } from '../'; + +const LinkControlSearchInput = ( { + value, + onChange, + onSelect, + renderSuggestions, + fetchSuggestions, + onReset, + onKeyDown, + onKeyPress, +} ) => { + const selectItemHandler = ( selection, suggestion ) => { + onChange( selection ); + + if ( suggestion ) { + onSelect( suggestion ); + } + }; + + const stopFormEventsPropagation = ( event ) => { + event.preventDefault(); + event.stopPropagation(); + }; + + return ( +
+ { + if ( event.keyCode === ENTER ) { + return; + } + onKeyDown( event ); + } } + onKeyPress={ onKeyPress } + placeholder={ __( 'Search or type url' ) } + __experimentalRenderSuggestions={ renderSuggestions } + __experimentalFetchLinkSuggestions={ fetchSuggestions } + __experimentalHandleURLSuggestions={ true } + /> + + + + + ); +}; + +export default LinkControlSearchInput; diff --git a/packages/block-editor/src/components/link-control/search-item.js b/packages/block-editor/src/components/link-control/search-item.js new file mode 100644 index 00000000000000..432b4bb3dff17a --- /dev/null +++ b/packages/block-editor/src/components/link-control/search-item.js @@ -0,0 +1,54 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * Internal dependencies + */ +import TextHighlight from './text-highlight'; + +/** + * WordPress dependencies + */ +import { safeDecodeURI } from '@wordpress/url'; +import { __ } from '@wordpress/i18n'; + +import { + Icon, +} from '@wordpress/components'; + +export const LinkControlSearchItem = ( { itemProps, suggestion, isSelected = false, onClick, isURL = false, searchTerm = '' } ) => { + return ( + + ); +}; + +export default LinkControlSearchItem; + diff --git a/packages/block-editor/src/components/link-control/settings-drawer.js b/packages/block-editor/src/components/link-control/settings-drawer.js new file mode 100644 index 00000000000000..372426e4e821ff --- /dev/null +++ b/packages/block-editor/src/components/link-control/settings-drawer.js @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { partial } from 'lodash'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + ToggleControl, +} from '@wordpress/components'; + +const LinkControlSettingsDrawer = ( { settings, onSettingChange } ) => { + if ( ! settings || settings.length ) { + return null; + } + + return ( +
+ +
+ ); +}; + +export default LinkControlSettingsDrawer; diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss new file mode 100644 index 00000000000000..69f87d79fdfe3d --- /dev/null +++ b/packages/block-editor/src/components/link-control/style.scss @@ -0,0 +1,202 @@ +.block-editor-link-control__search { + position: relative; + min-width: $modal-min-width; +} + +.block-editor-link-control__search .block-editor-link-control__search-input { + // Specificity overide + &.block-editor-link-control__search-input > input[type="text"] { + width: calc(100% - #{$grid-size-large*2}); + display: block; + padding: 11px $grid-size-large; + margin: $grid-size-large; + padding-right: 38px; // width of reset button + position: relative; + z-index: 1; + border: 1px solid #e1e1e1; + border-radius: $radius-round-rectangle; + + /* Fonts smaller than 16px causes mobile safari to zoom. */ + font-size: $mobile-text-min-font-size; + + @include break-small { + font-size: $default-font-size; + } + + &:focus { + @include input-style__focus(); + } + } +} + +.block-editor-link-control__search-reset { + position: absolute; + top: 19px; // has to be hard coded as form expands with search suggestions + right: 19px; // push away to avoid focus style obscuring input border + z-index: 10; +} + +.block-editor-link-control__search-results-wrapper { + position: relative; + margin-top: -$grid-size-large + 1px; + + &::before, + &::after { + content: ""; + position: absolute; + left: -1px; + right: $grid-size-large; // avoid overlaying scrollbars + display: block; + pointer-events: none; + z-index: 100; + } + + &::before { + height: $grid-size-large/2; + top: -1px; + bottom: auto; + background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); + } + + &::after { + height: 20px; + bottom: -1px; + top: auto; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); + } +} + +.block-editor-link-control__search-results { + margin: 0; + padding: $grid-size-large/2 $grid-size-large; + max-height: 200px; + overflow-y: scroll; // allow results list to scroll + + &.is-loading { + opacity: 0.2; + } +} + +.block-editor-link-control__search-item { + position: relative; + display: flex; + align-items: center; + font-size: $default-font-size; + cursor: pointer; + background: $white; + width: 100%; + border: none; + text-align: left; + padding: 10px 15px; + border-radius: 5px; + + &:hover, + &:focus { + background-color: #e9e9e9; + } + + &.is-selected { + background: #f2f2f2; + + .block-editor-link-control__search-item-type { + background: #fff; + } + } + + &.is-current { + background: transparent; + border: 0; + width: 100%; + cursor: default; + padding: $grid-size-large; + padding-left: $grid-size-xlarge; + } + + .block-editor-link-control__search-item-header { + display: block; + margin-right: $grid-size-xlarge; + } + + .block-editor-link-control__search-item-icon { + margin-right: 1em; + min-width: 24px; + } + + .block-editor-link-control__search-item-info, + .block-editor-link-control__search-item-title { + text-overflow: ellipsis; + max-width: 230px; + overflow: hidden; + white-space: nowrap; + } + + .block-editor-link-control__search-item-title { + display: block; + margin-bottom: 0.2em; + font-weight: 500; + + mark { + font-weight: 700; + color: #000; + background-color: transparent; + } + + span { + font-weight: normal; + } + } + + .block-editor-link-control__search-item-info { + display: block; + color: #999; + font-size: 0.9em; + line-height: 1.3; + } + + .block-editor-link-control__search-item-type { + display: block; + padding: 3px 8px; + margin-left: auto; + font-size: 0.9em; + background-color: #f3f4f5; + border-radius: 2px; + } +} + +// Specificity overide +.block-editor-link-control__search-results div[role="menu"] > .block-editor-link-control__search-item.block-editor-link-control__search-item { + padding: 10px; +} + +.block-editor-link-control__settings { + border-top: 1px solid #e1e1e1; + margin: 0; + padding: $grid-size-large $grid-size-xlarge; + + :last-child { + margin-bottom: 0; + } +} + +.block-editor-link-control .block-editor-link-control__search-input .components-spinner { + display: block; + z-index: 100; + float: none; + + &.components-spinner { // Specificity overide + position: absolute; + top: 70px; + left: 50%; + right: auto; + bottom: auto; + margin: 0 auto 16px auto; + transform: translateX(-50%); + } + + +} + +.block-editor-link-control__search-item-action { + margin-left: auto; // push to far right hand side + flex-shrink: 0; +} diff --git a/packages/block-editor/src/components/link-control/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/link-control/test/__snapshots__/index.js.snap new file mode 100644 index 00000000000000..df68c094eabc63 --- /dev/null +++ b/packages/block-editor/src/components/link-control/test/__snapshots__/index.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Basic rendering should display with required props 1`] = `"
"`; diff --git a/packages/block-editor/src/components/link-control/test/fixtures/index.js b/packages/block-editor/src/components/link-control/test/fixtures/index.js new file mode 100644 index 00000000000000..fc974749c982b1 --- /dev/null +++ b/packages/block-editor/src/components/link-control/test/fixtures/index.js @@ -0,0 +1,41 @@ +/** + * External dependencies + */ +import { uniqueId } from 'lodash'; + +export const fauxEntitySuggestions = [ + { + id: uniqueId(), + title: 'Hello Page', + type: 'Page', + info: '2 days ago', + url: `?p=${ uniqueId() }`, + }, + { + id: uniqueId(), + title: 'Hello Post', + type: 'Post', + info: '19 days ago', + url: `?p=${ uniqueId() }`, + }, + { + id: uniqueId(), + title: 'Hello Another One', + type: 'Page', + info: '19 days ago', + url: `?p=${ uniqueId() }`, + }, + { + id: uniqueId(), + title: 'This is another Post with a much longer title just to be really annoying and to try and break the UI', + type: 'Post', + info: '1 month ago', + url: `?p=${ uniqueId() }`, + }, +]; + +// export const fetchFauxEntitySuggestions = async () => fauxEntitySuggestions; + +export const fetchFauxEntitySuggestions = () => { + return Promise.resolve( fauxEntitySuggestions ); +}; diff --git a/packages/block-editor/src/components/link-control/test/index.js b/packages/block-editor/src/components/link-control/test/index.js new file mode 100644 index 00000000000000..d6dc506c7687b8 --- /dev/null +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -0,0 +1,527 @@ +/** + * External dependencies + */ +import { render, unmountComponentAtNode } from 'react-dom'; +import { act, Simulate } from 'react-dom/test-utils'; +import { first, last, nth } from 'lodash'; +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { UP, DOWN, ENTER } from '@wordpress/keycodes'; +/** + * Internal dependencies + */ +import LinkControl from '../index'; +import { fauxEntitySuggestions, fetchFauxEntitySuggestions } from './fixtures'; + +function eventLoopTick() { + return new Promise( ( resolve ) => setImmediate( resolve ) ); +} + +let container = null; + +beforeEach( () => { + // setup a DOM element as a render target + container = document.createElement( 'div' ); + document.body.appendChild( container ); +} ); + +afterEach( () => { + // cleanup on exiting + unmountComponentAtNode( container ); + container.remove(); + container = null; +} ); + +describe( 'Basic rendering', () => { + it( 'should display with required props', () => { + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // expect( searchInputLabel ).not.toBeNull(); + expect( searchInput ).not.toBeNull(); + + expect( container.innerHTML ).toMatchSnapshot(); + } ); +} ); + +describe( 'Searching for a link', () => { + it( 'should display loading UI when input is valid but search results have yet to be returned', async () => { + const searchTerm = 'Hello'; + + let resolver; + + const fauxRequest = () => new Promise( ( resolve ) => { + resolver = resolve; + } ); + + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + // fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + const searchResultElements = container.querySelectorAll( '[role="menu"] button[role="menuitem"]' ); + + let loadingUI = container.querySelector( '.components-spinner' ); + + expect( searchResultElements ).toHaveLength( 0 ); + + expect( loadingUI ).not.toBeNull(); + + act( () => { + resolver( fauxEntitySuggestions ); + } ); + + await eventLoopTick(); + + loadingUI = container.querySelector( '.components-spinner' ); + + expect( loadingUI ).toBeNull(); + } ); + + it( 'should display only search suggestions when current input value is not URL-like', async ( ) => { + const searchTerm = 'Hello world'; + const firstFauxSuggestion = first( fauxEntitySuggestions ); + + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + // fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + const searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + const firstSearchResultItemHTML = first( searchResultElements ).innerHTML; + const lastSearchResultItemHTML = last( searchResultElements ).innerHTML; + + expect( searchResultElements ).toHaveLength( fauxEntitySuggestions.length ); + + // Sanity check that a search suggestion shows up corresponding to the data + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( firstFauxSuggestion.title ) ); + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( firstFauxSuggestion.type ) ); + + // The fallback URL suggestion should not be shown when input is not URL-like + expect( lastSearchResultItemHTML ).not.toEqual( expect.stringContaining( 'URL' ) ); + } ); + + it.each( [ + [ 'couldbeurlorentitysearchterm' ], + [ 'ThisCouldAlsoBeAValidURL' ], + ] )( 'should display a URL suggestion as a default fallback for the search term "%s" which could potentially be a valid url.', async ( searchTerm ) => { + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + // fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + const searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + const lastSearchResultItemHTML = last( searchResultElements ).innerHTML; + const additionalDefaultFallbackURLSuggestionLength = 1; + + // We should see a search result for each of the expect search suggestions + // plus 1 additional one for the fallback URL suggestion + expect( searchResultElements ).toHaveLength( fauxEntitySuggestions.length + additionalDefaultFallbackURLSuggestionLength ); + + // The last item should be a URL search suggestion + expect( lastSearchResultItemHTML ).toEqual( expect.stringContaining( searchTerm ) ); + expect( lastSearchResultItemHTML ).toEqual( expect.stringContaining( 'URL' ) ); + expect( lastSearchResultItemHTML ).toEqual( expect.stringContaining( 'Press ENTER to add this link' ) ); + } ); + + it( 'should reset the input field and the search results when search term is cleared or reset', async ( ) => { + const searchTerm = 'Hello world'; + + act( () => { + render( + , container + ); + } ); + + let searchResultElements; + let searchInput; + + // Search Input UI + searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + // fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + + // Check we have definitely rendered some suggestions + expect( searchResultElements ).toHaveLength( fauxEntitySuggestions.length ); + + // Grab the reset button now it's available + const resetUI = container.querySelector( '[aria-label="Reset"]' ); + + act( () => { + Simulate.click( resetUI ); + } ); + + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + expect( searchInput.value ).toBe( '' ); + expect( searchResultElements ).toHaveLength( 0 ); + } ); +} ); + +describe( 'Manual link entry', () => { + it.each( [ + [ 'https://make.wordpress.org' ], // explicit https + [ 'http://make.wordpress.org' ], // explicit http + [ 'www.wordpress.org' ], // usage of "www" + ] )( 'should display a single suggestion result when the current input value is URL-like (eg: %s)', async ( searchTerm ) => { + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + // fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + const searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + const firstSearchResultItemHTML = searchResultElements[ 0 ].innerHTML; + const expectedResultsLength = 1; + + expect( searchResultElements ).toHaveLength( expectedResultsLength ); + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( searchTerm ) ); + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( 'URL' ) ); + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( 'Press ENTER to add this link' ) ); + } ); + + describe( 'Alternative link protocols and formats', () => { + it.each( [ + [ 'mailto:example123456@wordpress.org', 'mailto' ], + [ 'tel:example123456@wordpress.org', 'tel' ], + [ '#internal-anchor', 'internal' ], + ] )( 'should recognise "%s" as a %s link and handle as manual entry by displaying a single suggestion', async ( searchTerm, searchType ) => { + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + // fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + const searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + const firstSearchResultItemHTML = searchResultElements[ 0 ].innerHTML; + const expectedResultsLength = 1; + + expect( searchResultElements ).toHaveLength( expectedResultsLength ); + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( searchTerm ) ); + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( searchType ) ); + expect( firstSearchResultItemHTML ).toEqual( expect.stringContaining( 'Press ENTER to add this link' ) ); + } ); + } ); +} ); + +describe( 'Selecting links', () => { + it( 'should display a selected link corresponding to the provided "currentLink" prop', () => { + const selectedLink = first( fauxEntitySuggestions ); + + const LinkControlConsumer = () => { + const [ link ] = useState( selectedLink ); + + return ( + + ); + }; + + act( () => { + render( + , container + ); + } ); + + // TODO: select by aria role or visible text + const currentLink = container.querySelector( '.block-editor-link-control__search-item.is-current' ); + const currentLinkHTML = currentLink.innerHTML; + const currentLinkAnchor = currentLink.querySelector( `[href="${ selectedLink.url }"]` ); + + expect( currentLinkHTML ).toEqual( expect.stringContaining( selectedLink.title ) ); + expect( currentLinkHTML ).toEqual( expect.stringContaining( selectedLink.type ) ); + expect( currentLinkHTML ).toEqual( expect.stringContaining( 'Change' ) ); + expect( currentLinkAnchor ).not.toBeNull(); + } ); + + it( 'should remove currently selected link and (re)display search UI when "Change" button is clicked', () => { + const selectedLink = first( fauxEntitySuggestions ); + + const LinkControlConsumer = () => { + const [ link, setLink ] = useState( selectedLink ); + + return ( + setLink( suggestion ) } + fetchSearchSuggestions={ fetchFauxEntitySuggestions } + /> + ); + }; + + act( () => { + render( + , container + ); + } ); + + // TODO: select by aria role or visible text + let currentLink = container.querySelector( '.block-editor-link-control__search-item.is-current' ); + + const currentLinkBtn = currentLink.querySelector( 'button' ); + + // Simulate searching for a term + act( () => { + Simulate.click( currentLinkBtn ); + } ); + + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + currentLink = container.querySelector( '.block-editor-link-control__search-item.is-current' ); + + // We should be back to showing the search input + expect( searchInput ).not.toBeNull(); + expect( currentLink ).toBeNull(); + } ); + + describe( 'Selection using mouse click', () => { + it.each( [ + [ 'entity', 'hello world', first( fauxEntitySuggestions ) ], // entity search + [ 'url', 'https://www.wordpress.org', { + id: '1', + title: 'https://www.wordpress.org', + url: 'https://www.wordpress.org', + type: 'URL', + } ], // url + ] )( 'should display a current selected link UI when a %s suggestion for the search "%s" is clicked', async ( type, searchTerm, selectedLink ) => { + const LinkControlConsumer = () => { + const [ link, setLink ] = useState( null ); + + return ( + setLink( suggestion ) } + fetchSearchSuggestions={ fetchFauxEntitySuggestions } + /> + ); + }; + + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + // fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + const searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + + const firstSearchSuggestion = first( searchResultElements ); + + // Simulate selecting the first of the search suggestions + act( () => { + Simulate.click( firstSearchSuggestion ); + } ); + + const currentLink = container.querySelector( '.block-editor-link-control__search-item.is-current' ); + const currentLinkHTML = currentLink.innerHTML; + const currentLinkAnchor = currentLink.querySelector( `[href="${ selectedLink.url }"]` ); + + // Check that this suggestion is now shown as selected + expect( currentLinkHTML ).toEqual( expect.stringContaining( selectedLink.title ) ); + expect( currentLinkHTML ).toEqual( expect.stringContaining( 'Change' ) ); + expect( currentLinkAnchor ).not.toBeNull(); + } ); + } ); + + describe( 'Selection using keyboard', () => { + it.each( [ + [ 'entity', 'hello world', first( fauxEntitySuggestions ) ], // entity search + [ 'url', 'https://www.wordpress.org', { + id: '1', + title: 'https://www.wordpress.org', + url: 'https://www.wordpress.org', + type: 'URL', + } ], // url + ] )( 'should display a current selected link UI when an %s suggestion for the search "%s" is selected using the keyboard', async ( type, searchTerm, selectedLink ) => { + const LinkControlConsumer = () => { + const [ link, setLink ] = useState( null ); + + return ( + setLink( suggestion ) } + fetchSearchSuggestions={ fetchFauxEntitySuggestions } + /> + ); + }; + + act( () => { + render( + , container + ); + } ); + + // Search Input UI + const searchInput = container.querySelector( 'input[aria-label="URL"]' ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { target: { value: searchTerm } } ); + } ); + + //fetchFauxEntitySuggestions resolves on next "tick" of event loop + await eventLoopTick(); + + // Step down into the search results, highlighting the first result item + act( () => { + Simulate.keyDown( searchInput, { keyCode: DOWN } ); + } ); + + // TODO: select these by aria relationship to autocomplete rather than arbitary selector. + const searchResultElements = container.querySelectorAll( '[role="listbox"] [role="option"]' ); + const firstSearchSuggestion = first( searchResultElements ); + const secondSearchSuggestion = nth( searchResultElements, 1 ); + + let selectedSearchResultElement = container.querySelector( '[role="option"][aria-selected="true"]' ); + + // We should have highlighted the first item using the keyboard + expect( selectedSearchResultElement ).toEqual( firstSearchSuggestion ); + + // Only entity searches contain more than 1 suggestion + if ( type === 'entity' ) { + // Check we can go down again using the down arrow + act( () => { + Simulate.keyDown( searchInput, { keyCode: DOWN } ); + } ); + + selectedSearchResultElement = container.querySelector( '[role="option"][aria-selected="true"]' ); + + // We should have highlighted the first item using the keyboard + expect( selectedSearchResultElement ).toEqual( secondSearchSuggestion ); + + // Check we can go back up via up arrow + act( () => { + Simulate.keyDown( searchInput, { keyCode: UP } ); + } ); + + selectedSearchResultElement = container.querySelector( '[role="option"][aria-selected="true"]' ); + + // We should be back to highlighting the first search result again + expect( selectedSearchResultElement ).toEqual( firstSearchSuggestion ); + } + + // Commit the selected item as the current link + act( () => { + Simulate.keyDown( searchInput, { keyCode: ENTER } ); + } ); + + // Check that the suggestion selected via is now shown as selected + const currentLink = container.querySelector( '.block-editor-link-control__search-item.is-current' ); + const currentLinkHTML = currentLink.innerHTML; + const currentLinkAnchor = currentLink.querySelector( `[href="${ selectedLink.url }"]` ); + + expect( currentLinkHTML ).toEqual( expect.stringContaining( selectedLink.title ) ); + expect( currentLinkHTML ).toEqual( expect.stringContaining( 'Change' ) ); + expect( currentLinkAnchor ).not.toBeNull(); + } ); + } ); +} ); diff --git a/packages/block-editor/src/components/link-control/text-highlight.js b/packages/block-editor/src/components/link-control/text-highlight.js new file mode 100644 index 00000000000000..dc7b35a3d6d2bc --- /dev/null +++ b/packages/block-editor/src/components/link-control/text-highlight.js @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { escapeRegExp } from 'lodash'; + +/** + * WordPress dependencies + */ +import { + Fragment, +} from '@wordpress/element'; + +const TextHighlight = ( { text = '', highlight = '' } ) => { + if ( ! highlight.trim() ) { + return text; + } + + const regex = new RegExp( `(${ escapeRegExp( highlight ) })`, 'gi' ); + const parts = text.split( regex ); + return ( + + { parts.filter( ( part ) => part ).map( ( part, i ) => ( + regex.test( part ) ? { part } : { part } + ) ) } + + ); +}; + +export default TextHighlight; diff --git a/packages/block-editor/src/components/media-placeholder/README.md b/packages/block-editor/src/components/media-placeholder/README.md index b730b9af5cbc9c..58140cf99e7215 100644 --- a/packages/block-editor/src/components/media-placeholder/README.md +++ b/packages/block-editor/src/components/media-placeholder/README.md @@ -155,9 +155,9 @@ The argument of the callback is an object containing the following properties: ### value -Media ID (or media IDs if multiple is true) to be selected by default when opening the media library. +An object or an array of objects that contain media ID (`id` property) to be selected by default when opening the media library. -- Type: `Number|Array` +- Type: `Object|Array` - Required: No - Platform: Web diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index d142d6d6c70008..d4e98de6179b12 100644 --- a/packages/block-editor/src/components/media-placeholder/index.js +++ b/packages/block-editor/src/components/media-placeholder/index.js @@ -429,7 +429,7 @@ const applyWithSelect = withSelect( ( select ) => { const { getSettings } = select( 'core/block-editor' ); return { - mediaUpload: getSettings().__experimentalMediaUpload, + mediaUpload: getSettings().mediaUpload, }; } ); diff --git a/packages/block-editor/src/components/media-placeholder/styles.native.scss b/packages/block-editor/src/components/media-placeholder/styles.native.scss index a0b7445debf66b..03c7c73b2edfc9 100644 --- a/packages/block-editor/src/components/media-placeholder/styles.native.scss +++ b/packages/block-editor/src/components/media-placeholder/styles.native.scss @@ -19,10 +19,6 @@ background-color: $background-dark-secondary; } -.emptyStateContainerDark { - background-color: $background-dark-secondary; -} - .emptyStateTitle { text-align: center; margin-top: 8; diff --git a/packages/block-editor/src/components/media-upload/check.js b/packages/block-editor/src/components/media-upload/check.js index 6f2903be5b1e10..c1fb3398020f4c 100644 --- a/packages/block-editor/src/components/media-upload/check.js +++ b/packages/block-editor/src/components/media-upload/check.js @@ -14,6 +14,6 @@ export default withSelect( ( select ) => { const { getSettings } = select( 'core/block-editor' ); return { - hasUploadPermissions: !! getSettings().__experimentalMediaUpload, + hasUploadPermissions: !! getSettings().mediaUpload, }; } )( MediaUploadCheck ); diff --git a/packages/block-editor/src/components/media-upload/index.js b/packages/block-editor/src/components/media-upload/index.js index 1cb3e511097276..2318ef93d5e4f4 100644 --- a/packages/block-editor/src/components/media-upload/index.js +++ b/packages/block-editor/src/components/media-upload/index.js @@ -8,7 +8,7 @@ import { withFilters } from '@wordpress/components'; * an integration with the core blocks that handle media files. By default it renders nothing but * it provides a way to have it overridden with the `editor.MediaUpload` filter. * - * @return {WPElement} Media upload element. + * @return {WPComponent} The component to be rendered. */ const MediaUpload = () => null; diff --git a/packages/block-editor/src/components/media-upload/index.native.js b/packages/block-editor/src/components/media-upload/index.native.js index 3d89091e94da3d..2e1c3a9fcd8010 100644 --- a/packages/block-editor/src/components/media-upload/index.native.js +++ b/packages/block-editor/src/components/media-upload/index.native.js @@ -6,6 +6,8 @@ import { requestMediaPickFromMediaLibrary, requestMediaPickFromDeviceLibrary, requestMediaPickFromDeviceCamera, + getOtherMediaOptions, + requestOtherMediaPickFrom, } from 'react-native-gutenberg-bridge'; /** @@ -31,7 +33,27 @@ export class MediaUpload extends React.Component { this.onPickerPresent = this.onPickerPresent.bind( this ); this.onPickerChange = this.onPickerChange.bind( this ); this.onPickerSelect = this.onPickerSelect.bind( this ); + + this.state = { + otherMediaOptions: undefined, + }; + } + + componentDidMount() { + const { allowedTypes = [] } = this.props; + getOtherMediaOptions( allowedTypes, ( otherMediaOptions ) => { + const otherMediaOptionsWithIcons = otherMediaOptions.map( ( option ) => { + return { + icon: this.getChooseFromDeviceIcon(), + value: option.value, + label: option.label, + }; + } ); + + this.setState( { otherMediaOptions: otherMediaOptionsWithIcons } ); + } ); } + getTakeMediaLabel() { const { allowedTypes = [] } = this.props; @@ -98,15 +120,26 @@ export class MediaUpload extends React.Component { this.onPickerSelect( requestMediaPickFromDeviceCamera ); } else if ( value === MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_WORD_PRESS_LIBRARY ) { this.onPickerSelect( requestMediaPickFromMediaLibrary ); + } else { + const { onSelect, multiple = false } = this.props; + requestOtherMediaPickFrom( value, multiple, ( media ) => { + if ( ( multiple && media ) || ( media && media.id ) ) { + onSelect( media ); + } + } ); } } render() { - const mediaOptions = this.getMediaOptionsItems(); + let mediaOptions = this.getMediaOptionsItems(); + + if ( this.state.otherMediaOptions ) { + mediaOptions = [ ...mediaOptions, ...this.state.otherMediaOptions ]; + } const getMediaOptions = () => ( this.picker = instance } options={ mediaOptions } onChange={ this.onPickerChange } diff --git a/packages/block-editor/src/components/provider/index.js b/packages/block-editor/src/components/provider/index.js index 488dd6244d8c65..c2b29f13fa74ac 100644 --- a/packages/block-editor/src/components/provider/index.js +++ b/packages/block-editor/src/components/provider/index.js @@ -73,8 +73,8 @@ class BlockEditorProvider extends Component { * 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. + * @param {WPDataRegistry} registry Registry from which block editor + * dispatch is to be overridden. */ attachChangeObserver( registry ) { if ( this.unsubscribe ) { diff --git a/packages/block-editor/src/components/provider/index.native.js b/packages/block-editor/src/components/provider/index.native.js index 8e2f2e677397b4..fca4fab5ddbdee 100644 --- a/packages/block-editor/src/components/provider/index.native.js +++ b/packages/block-editor/src/components/provider/index.native.js @@ -75,8 +75,8 @@ class BlockEditorProvider extends Component { * 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. + * @param {WPDataRegistry} registry Registry from which block editor + * dispatch is to be overridden. */ attachChangeObserver( registry ) { if ( this.unsubscribe ) { diff --git a/packages/block-editor/src/components/rich-text/file-paste-handler.js b/packages/block-editor/src/components/rich-text/file-paste-handler.js new file mode 100644 index 00000000000000..eceeb069b263ff --- /dev/null +++ b/packages/block-editor/src/components/rich-text/file-paste-handler.js @@ -0,0 +1,11 @@ +/** + * WordPress dependencies + */ +import { createBlobURL } from '@wordpress/blob'; + +export function filePasteHandler( files ) { + return files + .filter( ( { type } ) => /^image\/(?:jpe?g|png|gif)$/.test( type ) ) + .map( ( file ) => `` ) + .join( '' ); +} diff --git a/packages/block-editor/src/components/rich-text/file-paste-handler.native.js b/packages/block-editor/src/components/rich-text/file-paste-handler.native.js new file mode 100644 index 00000000000000..41402a0dcda68c --- /dev/null +++ b/packages/block-editor/src/components/rich-text/file-paste-handler.native.js @@ -0,0 +1,3 @@ +export function filePasteHandler( files ) { + return files.map( ( url ) => `` ).join( '' ); +} diff --git a/packages/block-editor/src/components/rich-text/format-toolbar-container.js b/packages/block-editor/src/components/rich-text/format-toolbar-container.js new file mode 100644 index 00000000000000..fc857311706db3 --- /dev/null +++ b/packages/block-editor/src/components/rich-text/format-toolbar-container.js @@ -0,0 +1,59 @@ +/** + * WordPress dependencies + */ +import { Popover } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import BlockFormatControls from '../block-format-controls'; +import FormatToolbar from './format-toolbar'; + +function getAnchorRect( anchorObj ) { + const { current } = anchorObj; + const rect = current.getBoundingClientRect(); + + // Add some space. + const buffer = 6; + + // Subtract padding if any. + let { paddingTop } = window.getComputedStyle( current ); + + paddingTop = parseInt( paddingTop, 10 ); + + return { + x: rect.left, + y: rect.top + paddingTop - buffer, + width: rect.width, + height: rect.height - paddingTop + buffer, + left: rect.left, + right: rect.right, + top: rect.top + paddingTop - buffer, + bottom: rect.bottom, + }; +} + +const FormatToolbarContainer = ( { inline, anchorObj } ) => { + if ( inline ) { + // Render in popover + return ( + getAnchorRect( anchorObj ) } + className="block-editor-rich-text__inline-format-toolbar" + > + + + ); + } + // Render regular toolbar + return ( + + + + ); +}; + +export default FormatToolbarContainer; diff --git a/packages/block-editor/src/components/rich-text/format-toolbar-container.native.js b/packages/block-editor/src/components/rich-text/format-toolbar-container.native.js new file mode 100644 index 00000000000000..d37fe5bfee0f68 --- /dev/null +++ b/packages/block-editor/src/components/rich-text/format-toolbar-container.native.js @@ -0,0 +1,16 @@ +/** + * Internal dependencies + */ +import BlockFormatControls from '../block-format-controls'; +import FormatToolbar from './format-toolbar'; + +const FormatToolbarContainer = () => { + // Render regular toolbar + return ( + + + + ); +}; + +export default FormatToolbarContainer; diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index b78e8c3a46ffc3..53292f1402d5d7 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -7,9 +7,9 @@ import { omit } from 'lodash'; /** * WordPress dependencies */ -import { RawHTML, Component, createRef } from '@wordpress/element'; +import { RawHTML, Component, createRef, Platform } from '@wordpress/element'; import { withDispatch, withSelect } from '@wordpress/data'; -import { pasteHandler, children as childrenSource, getBlockTransforms, findTransform } from '@wordpress/blocks'; +import { pasteHandler, children as childrenSource, getBlockTransforms, findTransform, isUnmodifiedDefaultBlock } from '@wordpress/blocks'; import { withInstanceId, compose } from '@wordpress/compose'; import { __experimentalRichText as RichText, @@ -25,8 +25,7 @@ import { toHTMLString, slice, } from '@wordpress/rich-text'; -import { withFilters, Popover } from '@wordpress/components'; -import { createBlobURL } from '@wordpress/blob'; +import { withFilters } from '@wordpress/components'; import deprecated from '@wordpress/deprecated'; import { isURL } from '@wordpress/url'; @@ -34,10 +33,10 @@ import { isURL } from '@wordpress/url'; * Internal dependencies */ import Autocomplete from '../autocomplete'; -import BlockFormatControls from '../block-format-controls'; -import FormatToolbar from './format-toolbar'; import { withBlockEditContext } from '../block-edit/context'; import { RemoveBrowserShortcuts } from './remove-browser-shortcuts'; +import { filePasteHandler } from './file-paste-handler'; +import FormatToolbarContainer from './format-toolbar-container'; const wrapperClasses = 'editor-rich-text block-editor-rich-text'; const classes = 'editor-rich-text__editable block-editor-rich-text__editable'; @@ -66,7 +65,6 @@ class RichTextWrapper extends Component { this.onPaste = this.onPaste.bind( this ); this.onDelete = this.onDelete.bind( this ); this.inputRule = this.inputRule.bind( this ); - this.getAnchorRect = this.getAnchorRect.bind( this ); } onEnter( { value, onChange, shiftKey } ) { @@ -124,7 +122,7 @@ class RichTextWrapper extends Component { } } - onPaste( { value, onChange, html, plainText, image } ) { + onPaste( { value, onChange, html, plainText, files } ) { const { onReplace, onSplit, @@ -134,16 +132,18 @@ class RichTextWrapper extends Component { __unstableEmbedURLOnPaste, } = this.props; - if ( image && ! html ) { - const file = image.getAsFile ? image.getAsFile() : image; + // Only process file if no HTML is present. + // Note: a pasted file may have the URL as plain text. + if ( files && files.length && ! html ) { const content = pasteHandler( { - HTML: ``, + HTML: filePasteHandler( files ), mode: 'BLOCKS', tagName, } ); // Allows us to ask for this information when we get a report. - window.console.log( 'Received item:\n\n', file ); + // eslint-disable-next-line no-console + window.console.log( 'Received items:\n\n', files ); if ( onReplace && isEmpty( value ) ) { onReplace( content ); @@ -303,30 +303,6 @@ class RichTextWrapper extends Component { return formattingControls.map( ( name ) => `core/${ name }` ); } - getAnchorRect() { - const { current } = this.ref; - const rect = current.getBoundingClientRect(); - - // Add some space. - const buffer = 6; - - // Subtract padding if any. - let { paddingTop } = window.getComputedStyle( current ); - - paddingTop = parseInt( paddingTop, 10 ); - - return { - x: rect.left, - y: rect.top + paddingTop - buffer, - width: rect.width, - height: rect.height - paddingTop + buffer, - left: rect.left, - right: rect.right, - top: rect.top + paddingTop - buffer, - bottom: rect.bottom, - }; - } - render() { const { children, @@ -424,22 +400,7 @@ class RichTextWrapper extends Component { { ( { isSelected, value, onChange, Editable } ) => <> { children && children( { value, onChange } ) } - { isSelected && ! inlineToolbar && hasFormats && ( - - - - ) } - { isSelected && inlineToolbar && hasFormats && ( - - - - ) } + { isSelected && hasFormats && ( ) } { isSelected && } ( { clientId } ) ), + withBlockEditContext( ( { clientId, onCaretVerticalPositionChange, isSelected }, ownProps ) => { + if ( Platform.OS === 'web' ) { + return { clientId }; + } + return { + clientId, + blockIsSelected: ownProps.isSelected !== undefined ? ownProps.isSelected : isSelected, + onCaretVerticalPositionChange, + }; + } ), withSelect( ( select, { clientId, instanceId, @@ -495,6 +465,7 @@ const RichTextContainer = compose( [ getSelectionEnd, getSettings, didAutomaticChange, + __unstableGetBlockWithoutInnerBlocks, } = select( 'core/block-editor' ); const selectionStart = getSelectionStart(); @@ -509,6 +480,18 @@ const RichTextContainer = compose( [ isSelected = selectionStart.clientId === clientId; } + let extraProps = {}; + if ( Platform.OS === 'native' ) { + // If the block of this RichText is unmodified then it's a candidate for replacing when adding a new block. + // In order to fix https://github.com/wordpress-mobile/gutenberg-mobile/issues/1126, let's blur on unmount in that case. + // This apparently assumes functionality the BlockHlder actually + const block = clientId && __unstableGetBlockWithoutInnerBlocks( clientId ); + const shouldBlurOnUnmount = block && isSelected && isUnmodifiedDefaultBlock( block ); + extraProps = { + shouldBlurOnUnmount, + }; + } + return { canUserUseUnfilteredHTML: __experimentalCanUserUseUnfilteredHTML, isCaretWithinFormattedText: isCaretWithinFormattedText(), @@ -516,6 +499,7 @@ const RichTextContainer = compose( [ selectionEnd: isSelected ? selectionEnd.offset : undefined, isSelected, didAutomaticChange: didAutomaticChange(), + ...extraProps, }; } ), withDispatch( ( dispatch, { diff --git a/packages/block-editor/src/components/rich-text/index.native.js b/packages/block-editor/src/components/rich-text/index.native.js deleted file mode 100644 index feab0f747a9229..00000000000000 --- a/packages/block-editor/src/components/rich-text/index.native.js +++ /dev/null @@ -1,213 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { View } from 'react-native'; - -/** - * WordPress dependencies - */ -import { RawHTML } from '@wordpress/element'; -import { withDispatch, withSelect } from '@wordpress/data'; -import { pasteHandler, isUnmodifiedDefaultBlock } from '@wordpress/blocks'; -import { withInstanceId, compose } from '@wordpress/compose'; -import { __experimentalRichText as RichText } from '@wordpress/rich-text'; - -/** - * Internal dependencies - */ -import Autocomplete from '../autocomplete'; -import BlockFormatControls from '../block-format-controls'; -import FormatToolbar from './format-toolbar'; -import { withBlockEditContext } from '../block-edit/context'; - -const wrapperClasses = 'editor-rich-text block-editor-rich-text'; -const classes = 'editor-rich-text__editable block-editor-rich-text__editable'; - -function RichTextWraper( { - children, - tagName, - value: originalValue, - onChange: originalOnChange, - selectionStart, - selectionEnd, - onSelectionChange, - multiline, - inlineToolbar, - wrapperClassName, - className, - autocompleters, - onReplace, - onRemove, - onMerge, - onSplit, - isCaretWithinFormattedText, - onEnterFormattedText, - onExitFormattedText, - canUserUseUnfilteredHTML, - isSelected: originalIsSelected, - onCreateUndoLevel, - placeholder, - // From experimental filter. - ...experimentalProps -} ) { - const adjustedValue = originalValue; - const adjustedOnChange = originalOnChange; - - return ( - - { ( { isSelected, value, onChange } ) => - - { children && children( { value, onChange } ) } - { isSelected && ! inlineToolbar && ( - - - - ) } - - } - - ); -} - -const RichTextContainer = compose( [ - withInstanceId, - withBlockEditContext( ( { clientId, onCaretVerticalPositionChange, isSelected }, ownProps ) => { - return { - clientId, - blockIsSelected: ownProps.isSelected !== undefined ? ownProps.isSelected : isSelected, - onCaretVerticalPositionChange, - }; - } ), - withSelect( ( select, { - clientId, - instanceId, - identifier = instanceId, - isSelected, - blockIsSelected, - } ) => { - const { getFormatTypes } = select( 'core/rich-text' ); - const { - getSelectionStart, - getSelectionEnd, - __unstableGetBlockWithoutInnerBlocks, - } = select( 'core/block-editor' ); - - const selectionStart = getSelectionStart(); - const selectionEnd = getSelectionEnd(); - - if ( isSelected === undefined ) { - isSelected = ( - selectionStart.clientId === clientId && - selectionStart.attributeKey === identifier - ); - } - - // If the block of this RichText is unmodified then it's a candidate for replacing when adding a new block. - // In order to fix https://github.com/wordpress-mobile/gutenberg-mobile/issues/1126, let's blur on unmount in that case. - // This apparently assumes functionality the BlockHlder actually - const block = clientId && __unstableGetBlockWithoutInnerBlocks( clientId ); - const shouldBlurOnUnmount = block && isSelected && isUnmodifiedDefaultBlock( block ); - - return { - formatTypes: getFormatTypes(), - selectionStart: isSelected ? selectionStart.offset : undefined, - selectionEnd: isSelected ? selectionEnd.offset : undefined, - isSelected, - blockIsSelected, - shouldBlurOnUnmount, - }; - } ), - withDispatch( ( dispatch, { - clientId, - instanceId, - identifier = instanceId, - } ) => { - const { - __unstableMarkLastChangeAsPersistent, - selectionChange, - } = dispatch( 'core/block-editor' ); - - return { - onCreateUndoLevel: __unstableMarkLastChangeAsPersistent, - onSelectionChange( start, end ) { - selectionChange( clientId, identifier, start, end ); - }, - }; - } ), -] )( RichTextWraper ); - -RichTextContainer.Content = ( { value, format, tagName: Tag, multiline, ...props } ) => { - let content; - let html = value; - let MultilineTag; - - if ( multiline === true || multiline === 'p' || multiline === 'li' ) { - MultilineTag = multiline === true ? 'p' : multiline; - } - - if ( ! html && MultilineTag ) { - html = `<${ MultilineTag }>`; - } - - switch ( format ) { - case 'string': - content = { html }; - break; - } - - if ( Tag ) { - return { content }; - } - - return content; -}; - -RichTextContainer.isEmpty = ( value = '' ) => { - // Handle deprecated `children` and `node` sources. - if ( Array.isArray( value ) ) { - return ! value || value.length === 0; - } - - return value.length === 0; -}; - -RichTextContainer.Content.defaultProps = { - format: 'string', - value: '', -}; - -/** - * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/rich-text/README.md - */ -export default RichTextContainer; -export { RichTextShortcut } from './shortcut'; -export { RichTextToolbarButton } from './toolbar-button'; -export { __unstableRichTextInputEvent } from './input-event'; diff --git a/packages/block-editor/src/components/rich-text/remove-browser-shortcuts.js b/packages/block-editor/src/components/rich-text/remove-browser-shortcuts.js index 165566a5bbbd16..9eee7b641a0e61 100644 --- a/packages/block-editor/src/components/rich-text/remove-browser-shortcuts.js +++ b/packages/block-editor/src/components/rich-text/remove-browser-shortcuts.js @@ -40,6 +40,6 @@ const SHORTCUTS_ELEMENT = ( * Component which registered keyboard event handlers to prevent default * behaviors for key combinations otherwise handled internally by RichText. * - * @return {WPElement} WordPress element. + * @return {WPComponent} The component to be rendered. */ export const RemoveBrowserShortcuts = () => SHORTCUTS_ELEMENT; diff --git a/packages/block-editor/src/components/rich-text/remove-browser-shortcuts.native.js b/packages/block-editor/src/components/rich-text/remove-browser-shortcuts.native.js new file mode 100644 index 00000000000000..43a4b030646db4 --- /dev/null +++ b/packages/block-editor/src/components/rich-text/remove-browser-shortcuts.native.js @@ -0,0 +1 @@ +export const RemoveBrowserShortcuts = () => null; diff --git a/packages/block-editor/src/components/typewriter/index.js b/packages/block-editor/src/components/typewriter/index.js index 7600b2cd8747cd..d2869ab03b2c01 100644 --- a/packages/block-editor/src/components/typewriter/index.js +++ b/packages/block-editor/src/components/typewriter/index.js @@ -100,7 +100,7 @@ class Typewriter extends Component { * Maintains the scroll position after a selection change caused by a * keyboard event. * - * @param {SyntheticEvent} event Synthetic keyboard event. + * @param {WPSyntheticEvent} event Synthetic keyboard event. */ maintainCaretPosition( { keyCode } ) { if ( ! this.isSelectionEligibleForScroll() ) { diff --git a/packages/block-editor/src/components/url-input/index.js b/packages/block-editor/src/components/url-input/index.js index 9652df56c9da74..1c32d9c29eab34 100644 --- a/packages/block-editor/src/components/url-input/index.js +++ b/packages/block-editor/src/components/url-input/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { throttle } from 'lodash'; +import { throttle, isFunction } from 'lodash'; import classnames from 'classnames'; import scrollIntoView from 'dom-scroll-into-view'; @@ -14,6 +14,7 @@ import { UP, DOWN, ENTER, TAB } from '@wordpress/keycodes'; import { Spinner, withSpokenMessages, Popover } from '@wordpress/components'; import { withInstanceId, withSafeTimeout, compose } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; +import { isURL } from '@wordpress/url'; // Since URLInput is rendered in the context of other inputs, but should be // considered a separate modal node, prevent keyboard events from propagating @@ -21,12 +22,15 @@ import { withSelect } from '@wordpress/data'; const stopEventPropagation = ( event ) => event.stopPropagation(); class URLInput extends Component { - constructor( { autocompleteRef } ) { - super( ...arguments ); + constructor( props ) { + super( props ); this.onChange = this.onChange.bind( this ); this.onKeyDown = this.onKeyDown.bind( this ); - this.autocompleteRef = autocompleteRef || createRef(); + this.selectLink = this.selectLink.bind( this ); + this.handleOnClick = this.handleOnClick.bind( this ); + this.bindSuggestionNode = this.bindSuggestionNode.bind( this ); + this.autocompleteRef = props.autocompleteRef || createRef(); this.inputRef = createRef(); this.updateSuggestions = throttle( this.updateSuggestions.bind( this ), 200 ); @@ -45,6 +49,7 @@ class URLInput extends Component { // when already expanded if ( showSuggestions && selectedSuggestion !== null && ! this.scrollingIntoView ) { this.scrollingIntoView = true; + scrollIntoView( this.suggestionNodes[ selectedSuggestion ], this.autocompleteRef.current, { onlyScrollIfNeeded: true, } ); @@ -66,14 +71,17 @@ class URLInput extends Component { } updateSuggestions( value ) { - const { fetchLinkSuggestions } = this.props; + const { + __experimentalFetchLinkSuggestions: fetchLinkSuggestions, + __experimentalHandleURLSuggestions: handleURLSuggestions, + } = this.props; if ( ! fetchLinkSuggestions ) { return; } // Show the suggestions after typing at least 2 characters // and also for URLs - if ( value.length < 2 || /^https?:/.test( value ) ) { + if ( value.length < 2 || ( ! handleURLSuggestions && isURL( value ) ) ) { this.setState( { showSuggestions: false, selectedSuggestion: null, @@ -132,9 +140,13 @@ class URLInput extends Component { onKeyDown( event ) { const { showSuggestions, selectedSuggestion, suggestions, loading } = this.state; + // If the suggestions are not shown or loading, we shouldn't handle the arrow keys // We shouldn't preventDefault to allow block arrow keys navigation - if ( ! showSuggestions || ! suggestions.length || loading ) { + if ( + ( ! showSuggestions || ! suggestions.length || loading ) && + this.props.value + ) { // In the Windows version of Firefox the up and down arrows don't move the caret // within an input field like they do for Mac Firefox/Chrome/Safari. This causes // a form of focus trapping that is disruptive to the user experience. This disruption @@ -223,19 +235,64 @@ class URLInput extends Component { this.inputRef.current.focus(); } - static getDerivedStateFromProps( { disableSuggestions }, { showSuggestions } ) { + static getDerivedStateFromProps( { value, disableSuggestions }, { showSuggestions, selectedSuggestion } ) { + let shouldShowSuggestions = showSuggestions; + + const hasValue = value && value.length; + + if ( ! hasValue ) { + shouldShowSuggestions = false; + } + + if ( disableSuggestions === true ) { + shouldShowSuggestions = false; + } + return { - showSuggestions: disableSuggestions === true ? false : showSuggestions, + selectedSuggestion: hasValue ? selectedSuggestion : null, + showSuggestions: shouldShowSuggestions, }; } render() { - const { value = '', autoFocus = true, instanceId, className, id, isFullWidth, hasBorder } = this.props; - const { showSuggestions, suggestions, selectedSuggestion, loading } = this.state; + const { + instanceId, + className, + id, + isFullWidth, + hasBorder, + __experimentalRenderSuggestions: renderSuggestions, + placeholder = __( 'Paste URL or type to search' ), + value = '', + autoFocus = true, + } = this.props; + + const { + showSuggestions, + suggestions, + selectedSuggestion, + loading, + } = this.state; const suggestionsListboxId = `block-editor-url-input-suggestions-${ instanceId }`; const suggestionOptionIdPrefix = `block-editor-url-input-suggestion-${ instanceId }`; + const suggestionsListProps = { + id: suggestionsListboxId, + ref: this.autocompleteRef, + role: 'listbox', + }; + + const buildSuggestionItemProps = ( suggestion, index ) => { + return { + role: 'option', + tabIndex: '-1', + id: `${ suggestionOptionIdPrefix }-${ index }`, + ref: this.bindSuggestionNode( index ), + 'aria-selected': index === selectedSuggestion, + }; + }; + /* eslint-disable jsx-a11y/no-autofocus */ return (
} - { showSuggestions && !! suggestions.length && + { isFunction( renderSuggestions ) && showSuggestions && !! suggestions.length && renderSuggestions( { + suggestions, + selectedSuggestion, + suggestionsListProps, + buildSuggestionItemProps, + isLoading: loading, + handleSuggestionClick: this.handleOnClick, + } ) } + + { ! isFunction( renderSuggestions ) && showSuggestions && !! suggestions.length &&
{ suggestions.map( ( suggestion, index ) => ( @@ -311,10 +371,15 @@ export default compose( withSafeTimeout, withSpokenMessages, withInstanceId, - withSelect( ( select ) => { + withSelect( ( select, props ) => { + // If a link suggestions handler is already provided then + // bail + if ( isFunction( props.__experimentalFetchLinkSuggestions ) ) { + return; + } const { getSettings } = select( 'core/block-editor' ); return { - fetchLinkSuggestions: getSettings().__experimentalFetchLinkSuggestions, + __experimentalFetchLinkSuggestions: getSettings().__experimentalFetchLinkSuggestions, }; } ) )( URLInput ); diff --git a/packages/block-editor/src/hooks/anchor.js b/packages/block-editor/src/hooks/anchor.js index 9b78c39bbfeb02..5f8f1ca25879f5 100644 --- a/packages/block-editor/src/hooks/anchor.js +++ b/packages/block-editor/src/hooks/anchor.js @@ -56,9 +56,9 @@ export function addAttribute( settings ) { * Override the default edit UI to include a new block inspector control for * assigning the anchor ID, if block supports anchor. * - * @param {Function|Component} BlockEdit Original component. + * @param {WPComponent} BlockEdit Original component. * - * @return {string} Wrapped component. + * @return {WPComponent} Wrapped component. */ export const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => { return ( props ) => { diff --git a/packages/block-editor/src/hooks/custom-class-name.js b/packages/block-editor/src/hooks/custom-class-name.js index bac77a37ea42a2..dfbceb627389cc 100644 --- a/packages/block-editor/src/hooks/custom-class-name.js +++ b/packages/block-editor/src/hooks/custom-class-name.js @@ -47,9 +47,9 @@ export function addAttribute( settings ) { * Override the default edit UI to include a new block inspector control for * assigning the custom class name, if block supports custom class name. * - * @param {Function|Component} BlockEdit Original component. + * @param {WPComponent} BlockEdit Original component. * - * @return {string} Wrapped component. + * @return {WPComponent} Wrapped component. */ export const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => { return ( props ) => { diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js index 06eabc9f6f11f4..54233e3bda2e91 100644 --- a/packages/block-editor/src/store/defaults.js +++ b/packages/block-editor/src/store/defaults.js @@ -32,6 +32,7 @@ export const PREFERENCES_DEFAULTS = { * __experimentalEnableLegacyWidgetBlock boolean Whether the user has enabled the Legacy Widget Block * __experimentalEnableMenuBlock boolean Whether the user has enabled the Menu Block * __experimentalBlockDirectory boolean Whether the user has enabled the Block Directory + * __experimentalEnableFullSiteEditing boolean Whether the user has enabled Full Site Editing */ export const SETTINGS_DEFAULTS = { alignWide: false, @@ -152,80 +153,98 @@ export const SETTINGS_DEFAULTS = { __experimentalEnableLegacyWidgetBlock: false, __experimentalEnableMenuBlock: false, __experimentalBlockDirectory: false, + __experimentalEnableFullSiteEditing: false, gradients: [ { name: __( 'Vivid cyan blue to vivid purple' ), - gradient: 'linear-gradient(135deg, rgba(6, 147, 227, 1) 0%, rgb(155, 81, 224) 100%)', + gradient: 'linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)', + slug: 'vivid-cyan-blue-to-vivid-purple', }, { name: __( 'Vivid green cyan to vivid cyan blue' ), - gradient: 'linear-gradient(135deg, rgba(0, 208, 132, 1) 0%, rgba(6, 147, 227, 1) 100%)', + gradient: 'linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)', + slug: 'vivid-green-cyan-to-vivid-cyan-blue', }, { name: __( 'Light green cyan to vivid green cyan' ), - gradient: 'linear-gradient(135deg, rgb(122, 220, 180) 0%, rgb(0, 208, 130) 100%)', + gradient: 'linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)', + slug: 'light-green-cyan-to-vivid-green-cyan', }, { name: __( 'Luminous vivid amber to luminous vivid orange' ), - gradient: 'linear-gradient(135deg, rgba(252, 185, 0, 1) 0%, rgba(255, 105, 0, 1) 100%)', + gradient: 'linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%)', + slug: 'luminous-vivid-amber-to-luminous-vivid-orange', }, { name: __( 'Luminous vivid orange to vivid red' ), - gradient: 'linear-gradient(135deg, rgba(255, 105, 0, 1) 0%, rgb(207, 46, 46) 100%)', + gradient: 'linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%)', + slug: 'luminous-vivid-orange-to-vivid-red', }, { name: __( 'Very light gray to cyan bluish gray' ), - gradient: 'linear-gradient(135deg, rgb(238, 238, 238) 0%, rgb(169, 184, 195)', + gradient: 'linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%)', + slug: 'very-light-gray-to-cyan-bluish-gray', }, // The following use new, customized colors. { name: __( 'Cool to warm spectrum' ), - gradient: 'linear-gradient(135deg, rgb(74, 234, 220), rgb(151, 120, 209), rgb(207, 42, 186), rgb(238, 44, 130), rgb(251, 105, 98),rgb(254, 248, 76)', + gradient: 'linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%)', + slug: 'cool-to-warm-spectrum', }, { name: __( 'Blush light purple' ), - gradient: 'linear-gradient(135deg, rgb(255, 206, 236), rgb(152, 150, 240)', + gradient: 'linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%)', + slug: 'blush-light-purple', }, { name: __( 'Blush bordeaux' ), - gradient: 'linear-gradient(135deg, rgb(254, 205, 165), rgb(254, 45, 45), rgb(107, 0, 62)', + gradient: 'linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%)', + slug: 'blush-bordeaux', }, { name: __( 'Purple crush' ), - gradient: 'linear-gradient(135deg, rgb(52, 226, 228), rgb(71, 33, 251), rgb(171, 29, 254)', + gradient: 'linear-gradient(135deg,rgb(52,226,228) 0%,rgb(71,33,251) 50%,rgb(171,29,254) 100%)', + slug: 'purple-crush', }, { name: __( 'Luminous dusk' ), - gradient: 'linear-gradient(135deg, rgb(255, 203, 112), rgb(199, 81, 192), rgb(65, 88, 208)', + gradient: 'linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)', + slug: 'luminous-dusk', }, { name: __( 'Hazy dawn' ), - gradient: 'linear-gradient(135deg, rgb(250, 172, 168), rgb(218, 208, 236)', + gradient: 'linear-gradient(135deg,rgb(250,172,168) 0%,rgb(218,208,236) 100%)', + slug: 'hazy-dawn', }, { name: __( 'Pale ocean' ), - gradient: 'linear-gradient(135deg, rgb(255, 245, 203), rgb(182, 227, 212), rgb(51, 167, 181)', + gradient: 'linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%)', + slug: 'pale-ocean', }, { name: __( 'Electric grass' ), - gradient: 'linear-gradient(135deg, rgb(202, 248, 128), rgb(113, 206, 126)', + gradient: 'linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%)', + slug: 'electric-grass', }, { name: __( 'Subdued olive' ), - gradient: 'linear-gradient(135deg, rgb(250, 250, 225), rgb(103, 166, 113)', + gradient: 'linear-gradient(135deg,rgb(250,250,225) 0%,rgb(103,166,113) 100%)', + slug: 'subdued-olive', }, { name: __( 'Atomic cream' ), - gradient: 'linear-gradient(135deg, rgb(253, 215, 154), rgb(0, 74, 89)', + gradient: 'linear-gradient(135deg,rgb(253,215,154) 0%,rgb(0,74,89) 100%)', + slug: 'atomic-cream', }, { name: __( 'Nightshade' ), - gradient: 'linear-gradient(135deg, rgb(51, 9, 104), rgb(49, 205, 207)', + gradient: 'linear-gradient(135deg,rgb(51,9,104) 0%,rgb(49,205,207) 100%)', + slug: 'nightshade', }, { name: __( 'Midnight' ), - gradient: 'linear-gradient(135deg, rgb(2, 3, 129), rgb(40, 116, 252)', + gradient: 'linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%)', + slug: 'midnight', }, ], }; - diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index d0dd7a69a15973..76bec295096bc3 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -29,6 +29,16 @@ import { } from '@wordpress/blocks'; import { SVG, Rect, G, Path } from '@wordpress/components'; +/** + * A block selection object. + * + * @typedef {Object} WPBlockSelection + * + * @property {string} clientId A block client ID. + * @property {string} attributeKey A block attribute key. + * @property {number} offset A block attribute offset. + */ + // Module constants /** @@ -237,9 +247,9 @@ export const getGlobalBlockCount = createSelector( if ( ! blockName ) { return clientIds.length; } - return reduce( clientIds, ( count, clientId ) => { + return reduce( clientIds, ( accumulator, clientId ) => { const block = state.blocks.byClientId[ clientId ]; - return block.name === blockName ? count + 1 : count; + return block.name === blockName ? accumulator + 1 : accumulator; }, 0 ); }, ( state ) => [ @@ -281,14 +291,6 @@ export function getBlockCount( state, rootClientId ) { return getBlockOrder( state, rootClientId ).length; } -/** - * @typedef {WPBlockSelection} A block selection object. - * - * @property {string} clientId A block client ID. - * @property {string} attributeKey A block attribute key. - * @property {number} offset A block attribute offset. - */ - /** * Returns the current selection start block client ID, attribute key and text * offset. @@ -418,6 +420,30 @@ export function getBlockRootClientId( state, clientId ) { null; } +/** + * Given a block client ID, returns the list of all its parents from top to bottom. + * + * @param {Object} state Editor state. + * @param {string} clientId Block from which to find root client ID. + * + * @return {Array} ClientIDs of the parent blocks. + */ +export const getBlockParents = createSelector( + ( state, clientId ) => { + const parents = []; + let current = clientId; + while ( !! state.blocks.parents[ current ] ) { + current = state.blocks.parents[ current ]; + parents.push( current ); + } + + return parents.reverse(); + }, + ( state ) => [ + state.blocks.parents, + ] +); + /** * 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. * @@ -1123,9 +1149,9 @@ const canIncludeBlockTypeInInserter = ( state, blockType, rootClientId ) => { * @param {Object} state Editor state. * @param {?string} rootClientId Optional root client ID of block list. * - * @return {Editor.InserterItem[]} Items that appear in inserter. + * @return {WPEditorInserterItem[]} Items that appear in inserter. * - * @typedef {Object} Editor.InserterItem + * @typedef {Object} WPEditorInserterItem * @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. @@ -1284,6 +1310,22 @@ export const hasInserterItems = createSelector( ], ); +/** + * Returns the list of allowed inserter blocks for inner blocks children + * + * @param {Object} state Editor state. + * @param {?string} rootClientId Optional root client ID of block list. + * + * @return {Array?} The list of allowed block types or false. + */ +export const __experimentalGetAllowedBlocks = ( state, rootClientId = null ) => { + if ( ! rootClientId ) { + return false; + } + const { allowedBlocks } = getBlockListSettings( state, rootClientId ); + return allowedBlocks; +}; + /** * Returns the Block List settings of a block, if any exist. * diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 8ebb2e487a4f29..9c149f001cf7fa 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -3,6 +3,7 @@ @import "./components/block-inspector/style.scss"; @import "./components/block-list/style.scss"; @import "./components/block-list-appender/style.scss"; +@import "./components/block-breadcrumb/style.scss"; @import "./components/block-card/style.scss"; @import "./components/block-compare/style.scss"; @import "./components/block-mover/style.scss"; @@ -18,6 +19,7 @@ @import "./components/contrast-checker/style.scss"; @import "./components/default-block-appender/style.scss"; @import "./components/gradient-picker/control.scss"; +@import "./components/link-control/style.scss"; @import "./components/inner-blocks/style.scss"; @import "./components/inserter-with-shortcuts/style.scss"; @import "./components/inserter/style.scss"; diff --git a/packages/block-library/package.json b/packages/block-library/package.json index cf7b1171009db5..8d4cd8ba268545 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "2.9.0", + "version": "2.9.3", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/src/audio/edit.js b/packages/block-library/src/audio/edit.js index 15387acbd90c7a..b048722a70ecf7 100644 --- a/packages/block-library/src/audio/edit.js +++ b/packages/block-library/src/audio/edit.js @@ -214,10 +214,8 @@ class AudioEdit extends Component { export default compose( [ withSelect( ( select ) => { const { getSettings } = select( 'core/block-editor' ); - const { __experimentalMediaUpload } = getSettings(); - return { - mediaUpload: __experimentalMediaUpload, - }; + const { mediaUpload } = getSettings(); + return { mediaUpload }; } ), withNotices, ] )( AudioEdit ); diff --git a/packages/block-library/src/button/block.json b/packages/block-library/src/button/block.json index ff43c67db5e834..bc35b9ff5ee32e 100644 --- a/packages/block-library/src/button/block.json +++ b/packages/block-library/src/button/block.json @@ -49,6 +49,9 @@ "borderRadius": { "type": "number" }, + "gradient": { + "type": "string" + }, "customGradient": { "type": "string" } diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index 17908484218091..11d1deef862cfd 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -8,7 +8,6 @@ import classnames from 'classnames'; */ import { __ } from '@wordpress/i18n'; import { - Component, useCallback, } from '@wordpress/element'; import { @@ -24,13 +23,14 @@ import { withFallbackStyles, } from '@wordpress/components'; import { - URLInput, - RichText, + __experimentalGradientPickerPanel, + __experimentalUseGradient, ContrastChecker, InspectorControls, - withColors, PanelColorSettings, - __experimentalGradientPickerControl, + RichText, + URLInput, + withColors, } from '@wordpress/block-editor'; const { getComputedStyle } = window; @@ -74,177 +74,163 @@ function BorderPanel( { borderRadius = '', setAttributes } ) { ); } -class ButtonEdit extends Component { - constructor() { - super( ...arguments ); - this.nodeRef = null; - this.bindRef = this.bindRef.bind( this ); - this.onSetLinkRel = this.onSetLinkRel.bind( this ); - this.onToggleOpenInNewTab = this.onToggleOpenInNewTab.bind( this ); - } - - bindRef( node ) { - if ( ! node ) { - return; - } - this.nodeRef = node; - } - - onSetLinkRel( value ) { - this.props.setAttributes( { rel: value } ); - } - - onToggleOpenInNewTab( value ) { - const { rel } = this.props.attributes; - const linkTarget = value ? '_blank' : undefined; - - let updatedRel = rel; - if ( linkTarget && ! rel ) { - updatedRel = NEW_TAB_REL; - } else if ( ! linkTarget && rel === NEW_TAB_REL ) { - updatedRel = undefined; - } - - this.props.setAttributes( { - linkTarget, - rel: updatedRel, - } ); - } - - render() { - const { - attributes, - backgroundColor, - textColor, - setBackgroundColor, - setTextColor, - fallbackBackgroundColor, - fallbackTextColor, - setAttributes, - className, - instanceId, - isSelected, - } = this.props; - - const { - borderRadius, - linkTarget, - placeholder, - rel, - text, - title, - url, - customGradient, - } = attributes; +function ButtonEdit( { + attributes, + backgroundColor, + textColor, + setBackgroundColor, + setTextColor, + fallbackBackgroundColor, + fallbackTextColor, + setAttributes, + className, + instanceId, + isSelected, +} ) { + const { + borderRadius, + linkTarget, + placeholder, + rel, + text, + title, + url, + } = attributes; + const onSetLinkRel = useCallback( + ( value ) => { + setAttributes( { rel: value } ); + }, + [ setAttributes ] + ); - const linkId = `wp-block-button__inline-link-${ instanceId }`; + const onToggleOpenInNewTab = useCallback( + ( value ) => { + const newLinkTarget = value ? '_blank' : undefined; + + let updatedRel = rel; + if ( newLinkTarget && ! rel ) { + updatedRel = NEW_TAB_REL; + } else if ( ! newLinkTarget && rel === NEW_TAB_REL ) { + updatedRel = undefined; + } + + setAttributes( { + linkTarget: newLinkTarget, + rel: updatedRel, + } ); + }, + [ rel, setAttributes ] + ); + const { + gradientClass, + gradientValue, + setGradient, + } = __experimentalUseGradient(); - return ( -
- setAttributes( { text: value } ) } - withoutInteractiveFormatting - className={ classnames( - 'wp-block-button__link', { - 'has-background': backgroundColor.color || customGradient, - [ backgroundColor.class ]: ! customGradient && backgroundColor.class, - 'has-text-color': textColor.color, - [ textColor.class ]: textColor.class, - 'no-border-radius': borderRadius === 0, + const linkId = `wp-block-button__inline-link-${ instanceId }`; + return ( +
+ setAttributes( { text: value } ) } + withoutInteractiveFormatting + className={ classnames( + 'wp-block-button__link', { + 'has-background': backgroundColor.color || gradientValue, + [ backgroundColor.class ]: ! gradientValue && backgroundColor.class, + 'has-text-color': textColor.color, + [ textColor.class ]: textColor.class, + [ gradientClass ]: gradientClass, + 'no-border-radius': borderRadius === 0, + } + ) } + style={ { + ...( ! backgroundColor.color && gradientValue ? + { background: gradientValue } : + { backgroundColor: backgroundColor.color } + ), + color: textColor.color, + borderRadius: borderRadius ? borderRadius + 'px' : undefined, + } } + /> + + setAttributes( { url: value } ) } + disableSuggestions={ ! isSelected } + id={ linkId } + isFullWidth + hasBorder + /> + + + { + setAttributes( { customGradient: undefined } ); + setBackgroundColor( newColor ); + }, + label: __( 'Background Color' ), + }, + { + value: textColor.color, + onChange: setTextColor, + label: __( 'Text Color' ), + }, + ] } + > + + + <__experimentalGradientPickerPanel + onChange={ + ( newGradient ) => { + setGradient( newGradient ); + setBackgroundColor(); } - ) } - style={ { - backgroundColor: ! customGradient && backgroundColor.color, - background: customGradient, - color: textColor.color, - borderRadius: borderRadius ? borderRadius + 'px' : undefined, - } } + } + value={ gradientValue } + /> + - - setAttributes( { url: value } ) } - disableSuggestions={ ! isSelected } - id={ linkId } - isFullWidth - hasBorder + + - - - { - setAttributes( { customGradient: undefined } ); - setBackgroundColor( newColor ); - }, - label: __( 'Background Color' ), - }, - { - value: textColor.color, - onChange: setTextColor, - label: __( 'Text Color' ), - }, - ] } - > - - - - <__experimentalGradientPickerControl - onChange={ - ( newGradient ) => { - setAttributes( { - customGradient: newGradient, - backgroundColor: undefined, - customBackgroundColor: undefined, - } ); - } - } - value={ customGradient } - /> - - - - - - - -
- ); - } + + +
+ ); } export default compose( [ diff --git a/packages/block-library/src/button/save.js b/packages/block-library/src/button/save.js index bcf18ffc293242..59eaa9f201217e 100644 --- a/packages/block-library/src/button/save.js +++ b/packages/block-library/src/button/save.js @@ -9,6 +9,7 @@ import classnames from 'classnames'; import { RichText, getColorClassName, + __experimentalGetGradientClass, } from '@wordpress/block-editor'; export default function save( { attributes } ) { @@ -19,6 +20,7 @@ export default function save( { attributes } ) { customTextColor, customGradient, linkTarget, + gradient, rel, text, textColor, @@ -28,18 +30,20 @@ export default function save( { attributes } ) { const textClass = getColorClassName( 'color', textColor ); const backgroundClass = ! customGradient && getColorClassName( 'background-color', backgroundColor ); + const gradientClass = __experimentalGetGradientClass( gradient ); const buttonClasses = classnames( 'wp-block-button__link', { 'has-text-color': textColor || customTextColor, [ textClass ]: textClass, - 'has-background': backgroundColor || customBackgroundColor || customGradient, + 'has-background': backgroundColor || customBackgroundColor || customGradient || gradient, [ backgroundClass ]: backgroundClass, 'no-border-radius': borderRadius === 0, + [ gradientClass ]: gradientClass, } ); const buttonStyle = { - backgroundColor: backgroundClass || customGradient ? undefined : customBackgroundColor, background: customGradient ? customGradient : undefined, + backgroundColor: backgroundClass || customGradient || gradient ? undefined : customBackgroundColor, color: textClass ? undefined : customTextColor, borderRadius: borderRadius ? borderRadius + 'px' : undefined, }; diff --git a/packages/block-library/src/columns/deprecated.js b/packages/block-library/src/columns/deprecated.js index 56ffe5d90bc6a1..a344f72eaf69f3 100644 --- a/packages/block-library/src/columns/deprecated.js +++ b/packages/block-library/src/columns/deprecated.js @@ -66,7 +66,7 @@ export default [ ) ); }, migrate( attributes, innerBlocks ) { - const columns = innerBlocks.reduce( ( result, innerBlock ) => { + const columns = innerBlocks.reduce( ( accumulator, innerBlock ) => { const { originalContent } = innerBlock; let columnIndex = getDeprecatedLayoutColumn( originalContent ); @@ -74,13 +74,13 @@ export default [ columnIndex = 0; } - if ( ! result[ columnIndex ] ) { - result[ columnIndex ] = []; + if ( ! accumulator[ columnIndex ] ) { + accumulator[ columnIndex ] = []; } - result[ columnIndex ].push( innerBlock ); + accumulator[ columnIndex ].push( innerBlock ); - return result; + return accumulator; }, [] ); const migratedInnerBlocks = columns.map( ( columnBlocks ) => ( diff --git a/packages/block-library/src/columns/editor.scss b/packages/block-library/src/columns/editor.scss index dfa71f9f0589b3..60c6730fc2b088 100644 --- a/packages/block-library/src/columns/editor.scss +++ b/packages/block-library/src/columns/editor.scss @@ -12,18 +12,6 @@ } } -// Fullwide: show margin left/right to ensure there's room for the side UI. -// This is not a 1:1 preview with the front-end where these margins would presumably be zero. -[data-type="core/columns"][data-align="full"] .wp-block-columns > .editor-inner-blocks { - padding-left: $block-padding; - padding-right: $block-padding; - - @include break-small() { - padding-left: $block-container-side-padding; - padding-right: $block-container-side-padding; - } -} - .wp-block-columns { display: block; @@ -43,7 +31,14 @@ > [data-type="core/column"] > .editor-block-list__block-edit .block-core-columns { display: flex; flex-direction: column; - flex: 1 0 auto; + + // This flex rule fixes an issue in IE11. + flex: 1 1 auto; + + // IE11 does not support `position: sticky`, so we use it here to serve correct Flex rules to modern browsers. + @supports (position: sticky) { + flex: 1; + } } // Adjust the individual column block. @@ -181,10 +176,10 @@ div.block-core-columns.is-vertically-aligned-bottom { /** * Add extra padding when the parent block is selected, for easier interaction. */ -.block-editor-block-list__layout .block-editor-block-list__block[data-type="core/columns"].is-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks, -.block-editor-block-list__layout .block-editor-block-list__block[data-type="core/columns"].has-child-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks, -.block-editor-block-list__layout .block-editor-block-list__block[data-type="core/column"].is-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks, -.block-editor-block-list__layout .block-editor-block-list__block[data-type="core/column"].has-child-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks { +[data-type="core/columns"].is-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks, +[data-type="core/columns"].has-child-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks, +[data-type="core/column"].is-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks, +[data-type="core/column"].has-child-selected > .block-editor-block-list__block-edit > [data-block] > div > .block-editor-inner-blocks { padding: $block-padding; // Negate this padding for the placeholder. @@ -193,3 +188,16 @@ div.block-core-columns.is-vertically-aligned-bottom { width: calc(100% + #{$block-padding * 2}); } } + + +// Fullwide: show margin left/right to ensure there's room for the side UI. +// This is not a 1:1 preview with the front-end where these margins would presumably be zero. +[data-type="core/columns"][data-align="full"] .wp-block-columns { + padding-left: $block-padding; + padding-right: $block-padding; + + @include break-small() { + padding-left: $block-container-side-padding; + padding-right: $block-container-side-padding; + } +} diff --git a/packages/block-library/src/columns/utils.js b/packages/block-library/src/columns/utils.js index 77a0b7cf4375a8..a6773d469c98f7 100644 --- a/packages/block-library/src/columns/utils.js +++ b/packages/block-library/src/columns/utils.js @@ -87,9 +87,9 @@ export function getTotalColumnsWidth( blocks, totalBlockCount = blocks.length ) * @return {Object} Column widths. */ export function getColumnWidths( blocks, totalBlockCount = blocks.length ) { - return blocks.reduce( ( result, block ) => { + return blocks.reduce( ( accumulator, block ) => { const width = getEffectiveColumnWidth( block, totalBlockCount ); - return Object.assign( result, { [ block.clientId ]: width } ); + return Object.assign( accumulator, { [ block.clientId ]: width } ); }, {} ); } diff --git a/packages/block-library/src/cover/block.json b/packages/block-library/src/cover/block.json index 957a8264ef6e6e..67e87f54c8374b 100644 --- a/packages/block-library/src/cover/block.json +++ b/packages/block-library/src/cover/block.json @@ -31,6 +31,9 @@ }, "minHeight": { "type": "number" + }, + "customGradient": { + "type": "string" } } } diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 1994f4a57e0713..1bc69c5d9af860 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -39,6 +39,8 @@ import { PanelColorSettings, withColors, ColorPalette, + __experimentalGradientPickerControl, + __experimentalGradientPicker, } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { withDispatch } from '@wordpress/data'; @@ -110,7 +112,7 @@ const CoverHeightInput = withInstanceId( onBlur={ onBlurEvent } value={ temporaryInput !== null ? temporaryInput : value } min={ COVER_MIN_HEIGHT } - step="10" + step="1" /> ); @@ -219,12 +221,13 @@ class CoverEdit extends Component { } = this.props; const { backgroundType, + customGradient, dimRatio, focalPoint, hasParallax, id, - url, minHeight, + url, } = attributes; const onSelectMedia = ( media ) => { if ( ! media || ! media.url ) { @@ -282,14 +285,20 @@ class CoverEdit extends Component { minHeight: ( temporaryMinHeight || minHeight ), }; + if ( customGradient && ! url ) { + style.background = customGradient; + } + if ( focalPoint ) { style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`; } + const hasBackground = !! ( url || overlayColor.color || customGradient ); + const controls = ( <> - { !! ( url || overlayColor.color ) && ( + { hasBackground && ( <> @@ -348,7 +357,7 @@ class CoverEdit extends Component { ) } - { ( url || overlayColor.color ) && ( + { hasBackground && ( <> { + setAttributes( { + customGradient: undefined, + } ); + setOverlayColor( ...args ); + }, label: __( 'Overlay Color' ), } ] } > + <__experimentalGradientPickerControl + label={ __( 'Overlay Gradient' ) } + onChange={ + ( newGradient ) => { + setAttributes( { + customGradient: newGradient, + customOverlayColor: undefined, + overlayColor: undefined, + } ); + } + } + value={ customGradient } + /> { !! url && ( ); - if ( ! ( url || overlayColor.color ) ) { + if ( ! hasBackground ) { const placeholderIcon = ; const label = __( 'Cover' ); @@ -409,13 +436,29 @@ class CoverEdit extends Component { notices={ noticeUI } onError={ this.onUploadError } > - +
+ + <__experimentalGradientPicker + onChange={ + ( newGradient ) => { + setAttributes( { + customGradient: newGradient, + customOverlayColor: undefined, + overlayColor: undefined, + } ); + } + } + value={ customGradient } + clearable={ false } + /> +
); @@ -429,6 +472,7 @@ class CoverEdit extends Component { 'has-background-dim': dimRatio !== 0, 'has-parallax': hasParallax, [ overlayColor.class ]: overlayColor.class, + 'has-background-gradient': customGradient, } ); @@ -476,6 +520,13 @@ class CoverEdit extends Component { src={ url } /> ) } + { url && customGradient && dimRatio !== 0 && ( +
- - setAttributes( { content: value } ) } - onMerge={ mergeBlocks } - onSplit={ ( value ) => { - if ( ! value ) { - return createBlock( 'core/paragraph' ); - } + { InspectorControlsColorPanel } + + setAttributes( { content: value } ) } + onMerge={ mergeBlocks } + onSplit={ ( value ) => { + if ( ! value ) { + return createBlock( 'core/paragraph' ); + } - return createBlock( 'core/heading', { - ...attributes, - content: value, - } ); - } } - onReplace={ onReplace } - onRemove={ () => onReplace( [] ) } - className={ classnames( className, { - [ `has-text-align-${ align }` ]: align, - 'has-text-color': textColor.color, - [ textColor.class ]: textColor.class, - } ) } - placeholder={ placeholder || __( 'Write heading…' ) } - style={ { - color: textColor.color, - } } - /> + return createBlock( 'core/heading', { + ...attributes, + content: value, + } ); + } } + onReplace={ onReplace } + onRemove={ () => onReplace( [] ) } + className={ classnames( className, { + [ `has-text-align-${ align }` ]: align, + } ) } + placeholder={ placeholder || __( 'Write heading…' ) } + /> + ); } -export default compose( [ - withColors( 'backgroundColor', { textColor: 'color' } ), -] )( HeadingEdit ); +export default withFallbackStyles( ( node ) => { + let backgroundColor = getComputedStyle( node ).backgroundColor; + while ( backgroundColor === 'rgba(0, 0, 0, 0)' && node.parentNode ) { + node = node.parentNode; + backgroundColor = getComputedStyle( node ).backgroundColor; + } + return { backgroundColor }; +} )( HeadingEdit ); diff --git a/packages/block-library/src/image/constants.js b/packages/block-library/src/image/constants.js index cd5e82b323bed5..ec8c41b8b09c31 100644 --- a/packages/block-library/src/image/constants.js +++ b/packages/block-library/src/image/constants.js @@ -3,6 +3,6 @@ export const LINK_DESTINATION_NONE = 'none'; export const LINK_DESTINATION_MEDIA = 'media'; export const LINK_DESTINATION_ATTACHMENT = 'attachment'; export const LINK_DESTINATION_CUSTOM = 'custom'; -export const NEW_TAB_REL = 'noreferrer noopener'; +export const NEW_TAB_REL = [ 'noreferrer', 'noopener' ]; export const ALLOWED_MEDIA_TYPES = [ 'image' ]; export const DEFAULT_SIZE_SLUG = 'large'; diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index b5779e71180e7d..c5dfdc20b97148 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -25,7 +25,6 @@ import { NavigableMenu, PanelBody, Path, - Rect, ResizableBox, SelectControl, Spinner, @@ -70,9 +69,9 @@ import { speak } from '@wordpress/a11y'; * Internal dependencies */ import { createUpgradedEmbedBlock } from '../embed/util'; -import icon from './icon'; +import icon, { editImageIcon } from './icon'; import ImageSize from './image-size'; -import { getUpdatedLinkTargetSettings } from './utils'; +import { getUpdatedLinkTargetSettings, removeNewTabRel } from './utils'; /** * Module constants @@ -585,8 +584,8 @@ export class ImageEdit extends Component { sizeSlug, } = attributes; + const cleanRel = removeNewTabRel( rel ); const isExternal = isExternalImage( id, url ); - const editImageIcon = ( ); const controls = ( } @@ -931,7 +930,7 @@ export default compose( [ const { getSettings } = select( 'core/block-editor' ); const { attributes: { id }, isSelected } = props; const { - __experimentalMediaUpload, + mediaUpload, imageSizes, isRTL, maxWidth, @@ -942,7 +941,7 @@ export default compose( [ maxWidth, isRTL, imageSizes, - mediaUpload: __experimentalMediaUpload, + mediaUpload, }; } ), withViewportMatch( { isLargeViewport: 'medium' } ), diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index ce2c1affe275a5..1d9274c3754697 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -9,7 +9,7 @@ import { requestImageFailedRetryDialog, requestImageUploadCancelDialog, } from 'react-native-gutenberg-bridge'; -import { isEmpty } from 'lodash'; +import { isEmpty, map } from 'lodash'; /** * WordPress dependencies @@ -17,10 +17,12 @@ import { isEmpty } from 'lodash'; import { TextControl, ToggleControl, + SelectControl, Icon, Toolbar, ToolbarButton, PanelBody, + PanelActions, } from '@wordpress/components'; import { @@ -31,6 +33,7 @@ import { MEDIA_TYPE_IMAGE, BlockControls, InspectorControls, + BlockAlignmentToolbar, } from '@wordpress/block-editor'; import { __, sprintf } from '@wordpress/i18n'; import { isURL } from '@wordpress/url'; @@ -41,7 +44,7 @@ import { withPreferredColorScheme } from '@wordpress/compose'; * Internal dependencies */ import styles from './styles.scss'; -import SvgIcon from './icon'; +import SvgIcon, { editImageIcon } from './icon'; import SvgIconRetry from './icon-retry'; import { getUpdatedLinkTargetSettings } from './utils'; @@ -50,6 +53,19 @@ import { LINK_DESTINATION_NONE, } from './constants'; +const IMAGE_SIZE_THUMBNAIL = 'thumbnail'; +const IMAGE_SIZE_MEDIUM = 'medium'; +const IMAGE_SIZE_LARGE = 'large'; +const IMAGE_SIZE_FULL_SIZE = 'full'; +const DEFAULT_SIZE_SLUG = IMAGE_SIZE_LARGE; +const sizeOptionLabels = { + [ IMAGE_SIZE_THUMBNAIL ]: __( 'Thumbnail' ), + [ IMAGE_SIZE_MEDIUM ]: __( 'Medium' ), + [ IMAGE_SIZE_LARGE ]: __( 'Large' ), + [ IMAGE_SIZE_FULL_SIZE ]: __( 'Full Size' ), +}; +const sizeOptions = map( sizeOptionLabels, ( label, option ) => ( { value: option, label } ) ); + // Default Image ratio 4:3 const IMAGE_ASPECT_RATIO = 4 / 3; @@ -70,9 +86,11 @@ export class ImageEdit extends React.Component { this.updateImageURL = this.updateImageURL.bind( this ); this.onSetLinkDestination = this.onSetLinkDestination.bind( this ); this.onSetNewTab = this.onSetNewTab.bind( this ); + this.onSetSizeSlug = this.onSetSizeSlug.bind( this ); this.onImagePressed = this.onImagePressed.bind( this ); this.onClearSettings = this.onClearSettings.bind( this ); this.onFocusCaption = this.onFocusCaption.bind( this ); + this.updateAlignment = this.updateAlignment.bind( this ); } componentDidMount() { @@ -86,14 +104,18 @@ export class ImageEdit extends React.Component { console.warn( 'Attributes has id with no url.' ); } + // Detect any pasted image and start an upload + if ( ! attributes.id && attributes.url && attributes.url.indexOf( 'file:' ) === 0 ) { + requestMediaImport( attributes.url, ( id, url ) => { + if ( url ) { + setAttributes( { id, url } ); + } + } ); + } + + // Make sure we mark any temporary images as failed if they failed while + // the editor wasn't open if ( attributes.id && attributes.url && ! isURL( attributes.url ) ) { - if ( attributes.url.indexOf( 'file:' ) === 0 ) { - requestMediaImport( attributes.url, ( id, url ) => { - if ( url ) { - setAttributes( { id, url } ); - } - } ); - } mediaUploadSync(); } } @@ -167,6 +189,10 @@ export class ImageEdit extends React.Component { this.props.setAttributes( { url, width: undefined, height: undefined } ); } + updateAlignment( nextAlign ) { + this.props.setAttributes( { align: nextAlign } ); + } + onSetLinkDestination( href ) { this.props.setAttributes( { linkDestination: LINK_DESTINATION_CUSTOM, @@ -179,12 +205,19 @@ export class ImageEdit extends React.Component { this.props.setAttributes( updatedLinkTarget ); } + onSetSizeSlug( sizeSlug ) { + this.props.setAttributes( { + sizeSlug, + } ); + } + onClearSettings() { this.props.setAttributes( { alt: '', linkDestination: LINK_DESTINATION_NONE, href: undefined, linkTarget: undefined, + sizeSlug: DEFAULT_SIZE_SLUG, rel: undefined, } ); } @@ -216,17 +249,24 @@ export class ImageEdit extends React.Component { render() { const { attributes, isSelected } = this.props; - const { url, height, width, alt, href, id, linkTarget } = attributes; + const { align, url, height, width, alt, href, id, linkTarget, sizeSlug } = attributes; + + const actions = [ { label: __( 'Clear All Settings' ), onPress: this.onClearSettings } ]; const getToolbarEditButton = ( open ) => ( + ); @@ -249,21 +289,26 @@ export class ImageEdit extends React.Component { checked={ linkTarget === '_blank' } onChange={ this.onSetNewTab } /> + { // eslint-disable-next-line no-undef + __DEV__ && + this.onSetSizeSlug( newValue ) } + options={ sizeOptions } + /> } - + ); @@ -280,6 +325,14 @@ export class ImageEdit extends React.Component { ); } + const alignToFlex = { + left: 'flex-start', + center: 'center', + right: 'flex-end', + full: 'center', + wide: 'center', + }; + const imageContainerHeight = Dimensions.get( 'window' ).width / IMAGE_ASPECT_RATIO; const getImageComponent = ( openMediaOptions, getMediaOptions ) => ( + { ! imageWidthWithinContainer && { this.getIcon( false ) } diff --git a/packages/block-library/src/image/icon.js b/packages/block-library/src/image/icon.js index b029bab8fbe98a..44f88783d24cfa 100644 --- a/packages/block-library/src/image/icon.js +++ b/packages/block-library/src/image/icon.js @@ -1,6 +1,8 @@ /** * WordPress dependencies */ -import { Path, SVG } from '@wordpress/components'; +import { Path, Rect, SVG } from '@wordpress/components'; export default ; + +export const editImageIcon = ( ); diff --git a/packages/block-library/src/image/save.js b/packages/block-library/src/image/save.js index 01aff39769152b..b3976c76a8de85 100644 --- a/packages/block-library/src/image/save.js +++ b/packages/block-library/src/image/save.js @@ -2,6 +2,7 @@ * External dependencies */ import classnames from 'classnames'; +import { isEmpty } from 'lodash'; /** * WordPress dependencies @@ -24,6 +25,8 @@ export default function save( { attributes } ) { sizeSlug, } = attributes; + const newRel = isEmpty( rel ) ? undefined : rel; + const classes = classnames( { [ `align${ align }` ]: align, [ `size-${ sizeSlug }` ]: sizeSlug, @@ -47,7 +50,7 @@ export default function save( { attributes } ) { className={ linkClass } href={ href } target={ linkTarget } - rel={ rel } + rel={ newRel } > { image } diff --git a/packages/block-library/src/image/styles.native.scss b/packages/block-library/src/image/styles.native.scss index 9c5a9cbf5c45e5..24b20de69dc8f8 100644 --- a/packages/block-library/src/image/styles.native.scss +++ b/packages/block-library/src/image/styles.native.scss @@ -18,10 +18,6 @@ align-items: center; } -.clearSettingsButton { - color: $alert-red; -} - .modalIcon { width: 80px; height: 80px; @@ -44,3 +40,13 @@ .iconDark { fill: $white; } + +.content { + flex: 1; +} + +.contentCentered { + flex: 1; + justify-content: center; + align-items: center; +} diff --git a/packages/block-library/src/image/test/edit.native.js b/packages/block-library/src/image/test/edit.native.js index d585f1f1396a9e..9c21c10d90dbaf 100644 --- a/packages/block-library/src/image/test/edit.native.js +++ b/packages/block-library/src/image/test/edit.native.js @@ -40,16 +40,16 @@ describe( 'Image Block', () => { instance.onSetNewTab( true ); - expect( setAttributes ).toBeCalledWith( { linkTarget: '_blank', rel: NEW_TAB_REL } ); + expect( setAttributes ).toHaveBeenCalledWith( { linkTarget: '_blank', rel: undefined } ); } ); it( 'unset link target', () => { - const component = renderer.create( getImageComponent( { linkTarget: '_blank', rel: NEW_TAB_REL } ) ); + const component = renderer.create( getImageComponent( { linkTarget: '_blank', rel: NEW_TAB_REL.join( ' ' ) } ) ); const instance = component.getInstance(); instance.onSetNewTab( false ); - expect( setAttributes ).toBeCalledWith( { linkTarget: undefined, rel: undefined } ); + expect( setAttributes ).toHaveBeenCalledWith( { linkTarget: undefined, rel: undefined } ); } ); } ); diff --git a/packages/block-library/src/image/utils.js b/packages/block-library/src/image/utils.js index 2cb9b8f5ca5c78..1e3e8a07fc3748 100644 --- a/packages/block-library/src/image/utils.js +++ b/packages/block-library/src/image/utils.js @@ -1,3 +1,11 @@ +/** + * External dependencies + */ +import { + isEmpty, + each, +} from 'lodash'; + /** * Internal dependencies */ @@ -12,6 +20,30 @@ export function calculatePreferedImageSize( image, container ) { return { width, height }; } +export function removeNewTabRel( currentRel ) { + let newRel = currentRel; + + if ( currentRel !== undefined && ! isEmpty( newRel ) ) { + if ( ! isEmpty( newRel ) ) { + each( NEW_TAB_REL, function( relVal ) { + const regExp = new RegExp( '\\b' + relVal + '\\b', 'gi' ); + newRel = newRel.replace( regExp, '' ); + } ); + + // Only trim if NEW_TAB_REL values was replaced. + if ( newRel !== currentRel ) { + newRel = newRel.trim(); + } + + if ( isEmpty( newRel ) ) { + newRel = undefined; + } + } + } + + return newRel; +} + /** * Helper to get the link target settings to be stored. * @@ -24,11 +56,11 @@ export function calculatePreferedImageSize( image, container ) { export function getUpdatedLinkTargetSettings( value, { rel } ) { const linkTarget = value ? '_blank' : undefined; - let updatedRel = rel; - if ( linkTarget && ! rel ) { - updatedRel = NEW_TAB_REL; - } else if ( ! linkTarget && rel === NEW_TAB_REL ) { + let updatedRel; + if ( ! linkTarget && ! rel ) { updatedRel = undefined; + } else { + updatedRel = removeNewTabRel( rel ); } return { diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index e49f68553b0a6f..3147ebcc0e9cdc 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -62,6 +62,9 @@ import * as classic from './classic'; import * as socialLinks from './social-links'; import * as socialLink from './social-link'; +// Full Site Editing Blocks +import * as siteTitle from './site-title'; + /** * Function to register an individual block. * @@ -162,14 +165,24 @@ export const registerCoreBlocks = () => { * __experimentalRegisterExperimentalCoreBlocks( settings ); * ``` */ -export const __experimentalRegisterExperimentalCoreBlocks = process.env.GUTENBERG_PHASE === 2 ? ( settings ) => { - const { __experimentalEnableLegacyWidgetBlock, __experimentalEnableMenuBlock } = settings; +export const __experimentalRegisterExperimentalCoreBlocks = + process.env.GUTENBERG_PHASE === 2 ? + ( settings ) => { + const { + __experimentalEnableLegacyWidgetBlock, + __experimentalEnableMenuBlock, + __experimentalEnableFullSiteEditing, + } = settings - [ - __experimentalEnableLegacyWidgetBlock ? legacyWidget : null, - __experimentalEnableMenuBlock ? navigationMenu : null, - __experimentalEnableMenuBlock ? navigationMenuItem : null, - socialLinks, - ...socialLink.sites, - ].forEach( registerBlock ); -} : undefined; + ;[ + __experimentalEnableLegacyWidgetBlock ? legacyWidget : null, + __experimentalEnableMenuBlock ? navigationMenu : null, + __experimentalEnableMenuBlock ? navigationMenuItem : null, + socialLinks, + ...socialLink.sites, + + // Register Full Site Editing Blocks. + ...( __experimentalEnableFullSiteEditing ? [ siteTitle ] : [] ), + ].forEach( registerBlock ); + } : + undefined; diff --git a/packages/block-library/src/index.native.js b/packages/block-library/src/index.native.js index 2702becc3a8c31..0d06a9df9f11af 100644 --- a/packages/block-library/src/index.native.js +++ b/packages/block-library/src/index.native.js @@ -96,9 +96,9 @@ export const coreBlocks = [ textColumns, verse, video, -].reduce( ( memo, block ) => { - memo[ block.name ] = block; - return memo; +].reduce( ( accumulator, block ) => { + accumulator[ block.name ] = block; + return accumulator; }, {} ); /** diff --git a/packages/block-library/src/list/block.json b/packages/block-library/src/list/block.json index 3dcae3a6ae5c22..71bca30f1a8ac3 100644 --- a/packages/block-library/src/list/block.json +++ b/packages/block-library/src/list/block.json @@ -14,6 +14,9 @@ "__unstableMultilineWrapperTags": [ "ol", "ul" ], "default": "" }, + "type": { + "type": "string" + }, "start": { "type": "number" }, diff --git a/packages/block-library/src/list/edit.js b/packages/block-library/src/list/edit.js index 6a76a376f617d3..4373bbec0ce437 100644 --- a/packages/block-library/src/list/edit.js +++ b/packages/block-library/src/list/edit.js @@ -32,7 +32,7 @@ export default function ListEdit( { onReplace, className, } ) { - const { ordered, values, reversed, start } = attributes; + const { ordered, values, type, reversed, start } = attributes; const tagName = ordered ? 'ol' : 'ul'; const controls = ( { value, onChange } ) => ( @@ -124,12 +124,13 @@ export default function ListEdit( { className={ className } placeholder={ __( 'Write list…' ) } onMerge={ mergeBlocks } - onSplit={ ( value ) => createBlock( name, { ordered, values: value } ) } + onSplit={ ( value ) => createBlock( name, { ...attributes, values: value } ) } __unstableOnSplitMiddle={ () => createBlock( 'core/paragraph' ) } onReplace={ onReplace } onRemove={ () => onReplace( [] ) } start={ start } reversed={ reversed } + type={ type } > { controls } diff --git a/packages/block-library/src/list/editor.scss b/packages/block-library/src/list/editor.scss deleted file mode 100644 index fffff9ebaf6ee1..00000000000000 --- a/packages/block-library/src/list/editor.scss +++ /dev/null @@ -1,5 +0,0 @@ -.editor-styles-wrapper div[data-type="core/list"] ul, -.editor-styles-wrapper div[data-type="core/list"] ol { - padding-left: 1.3em; - margin-left: 1.3em; -} diff --git a/packages/block-library/src/list/save.js b/packages/block-library/src/list/save.js index 18458c2c1ce5a5..8cd1d3870148ba 100644 --- a/packages/block-library/src/list/save.js +++ b/packages/block-library/src/list/save.js @@ -4,13 +4,14 @@ import { RichText } from '@wordpress/block-editor'; export default function save( { attributes } ) { - const { ordered, values, reversed, start } = attributes; + const { ordered, values, type, reversed, start } = attributes; const tagName = ordered ? 'ol' : 'ul'; return ( { - if ( url ) { - onMediaUpdate( { id, url } ); - } - } ); - } + const { mediaId, mediaUrl } = this.props; + + // Make sure we mark any temporary images as failed if they failed while + // the editor wasn't open + if ( mediaId && mediaUrl && mediaUrl.indexOf( 'file:' ) === 0 ) { mediaUploadSync(); } } @@ -161,6 +154,8 @@ class MediaContainer extends Component { const { finalWidth, finalHeight, imageWidthWithinContainer, isUploadFailed, retryMessage } = params; const opacity = isUploadInProgress ? 0.3 : 1; + const contentStyle = ! imageWidthWithinContainer ? styles.content : styles.contentCentered; + return ( - + { ! imageWidthWithinContainer && { this.getIcon( false ) } diff --git a/packages/block-library/src/media-text/style.native.scss b/packages/block-library/src/media-text/style.native.scss index 06d29dc715dab7..27c972f6f8d6e7 100644 --- a/packages/block-library/src/media-text/style.native.scss +++ b/packages/block-library/src/media-text/style.native.scss @@ -32,6 +32,12 @@ flex: 1; } +.contentCentered { + flex: 1; + justify-content: center; + align-items: center; +} + .imageContainer { align-items: center; background-color: $gray-lighten-30; diff --git a/packages/block-library/src/missing/edit.native.js b/packages/block-library/src/missing/edit.native.js index cd4898a4e8c692..0aa4c3436d7dfe 100644 --- a/packages/block-library/src/missing/edit.native.js +++ b/packages/block-library/src/missing/edit.native.js @@ -27,6 +27,9 @@ export class UnsupportedBlockEdit extends Component { const title = blockType ? blockType.settings.title : __( 'Unsupported' ); const titleStyle = getStylesFromColorScheme( styles.unsupportedBlockMessage, styles.unsupportedBlockMessageDark ); + const subTitleStyle = getStylesFromColorScheme( styles.unsupportedBlockSubtitle, styles.unsupportedBlockSubtitleDark ); + const subtitle = blockType ? { __( 'Unsupported' ) } : null; + const icon = blockType ? normalizeIconObject( blockType.settings.icon ) : 'admin-plugins'; const iconStyle = getStylesFromColorScheme( styles.unsupportedBlockIcon, styles.unsupportedBlockIconDark ); const iconClassName = 'unsupported-icon' + '-' + preferredColorScheme; @@ -34,6 +37,7 @@ export class UnsupportedBlockEdit extends Component { { title } + { subtitle } ); } diff --git a/packages/block-library/src/missing/style.native.scss b/packages/block-library/src/missing/style.native.scss index 63cd4258cd23b0..5967ceb1e4d9fe 100644 --- a/packages/block-library/src/missing/style.native.scss +++ b/packages/block-library/src/missing/style.native.scss @@ -27,12 +27,24 @@ } .unsupportedBlockMessage { - margin-top: 2; + margin-top: 4; text-align: center; color: $gray-dark; font-size: 14; + font-weight: 600; } .unsupportedBlockMessageDark { color: $white; } + +.unsupportedBlockSubtitle { + margin-top: 2; + text-align: center; + color: $gray-darken-20; + font-size: 12; +} + +.unsupportedBlockSubtitleDark { + color: $gray-20; +} diff --git a/packages/block-library/src/navigation-menu-item/block.json b/packages/block-library/src/navigation-menu-item/block.json index 915c63931cb2be..20b33eca1f4a05 100644 --- a/packages/block-library/src/navigation-menu-item/block.json +++ b/packages/block-library/src/navigation-menu-item/block.json @@ -5,9 +5,6 @@ "label": { "type": "string" }, - "destination": { - "type": "string" - }, "nofollow": { "type": "boolean", "default": false @@ -21,6 +18,9 @@ "opensInNewTab": { "type": "boolean", "default": false + }, + "url": { + "type": "string" } } } diff --git a/packages/block-library/src/navigation-menu-item/edit.js b/packages/block-library/src/navigation-menu-item/edit.js index f4e456321347c6..4d2258d08eb163 100644 --- a/packages/block-library/src/navigation-menu-item/edit.js +++ b/packages/block-library/src/navigation-menu-item/edit.js @@ -1,93 +1,140 @@ /** * External dependencies */ -import { invoke } from 'lodash'; +import classnames from 'classnames'; /** * WordPress dependencies */ import { withSelect } from '@wordpress/data'; import { - Dropdown, ExternalLink, - IconButton, PanelBody, TextareaControl, TextControl, + Toolbar, ToggleControl, + ToolbarButton, } from '@wordpress/components'; +import { + LEFT, + RIGHT, + UP, + DOWN, + BACKSPACE, + ENTER, +} from '@wordpress/keycodes'; import { __ } from '@wordpress/i18n'; import { + BlockControls, InnerBlocks, InspectorControls, - PlainText, + URLPopover, } from '@wordpress/block-editor'; import { Fragment, - useCallback, useRef, + useState, } from '@wordpress/element'; -/** - * Internal dependencies - */ -import MenuItemActions from './menu-item-actions'; -const POPOVER_PROPS = { noArrow: true }; - function NavigationMenuItemEdit( { attributes, - clientId, isSelected, isParentOfSelectedBlock, setAttributes, } ) { const plainTextRef = useRef( null ); - const onEditLableClicked = useCallback( - ( onClose ) => () => { - onClose(); - invoke( plainTextRef, [ 'current', 'textarea', 'focus' ] ); - }, - [ plainTextRef ] - ); + const [ isLinkOpen, setIsLinkOpen ] = useState( false ); + const [ isEditingLink, setIsEditingLink ] = useState( false ); + const [ urlInput, setUrlInput ] = useState( null ); + + const inputValue = urlInput !== null ? urlInput : url; + + const onKeyDown = ( event ) => { + if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( event.keyCode ) > -1 ) { + // Stop the key event from propagating up to ObserveTyping.startTypingInTextField. + event.stopPropagation(); + } + }; + + const closeURLPopover = () => { + setIsEditingLink( false ); + setUrlInput( null ); + setIsLinkOpen( false ); + }; + + const autocompleteRef = useRef( null ); + + const onFocusOutside = ( event ) => { + const autocompleteElement = autocompleteRef.current; + if ( autocompleteElement && autocompleteElement.contains( event.target ) ) { + return; + } + closeURLPopover(); + }; + + const stopPropagation = ( event ) => { + event.stopPropagation(); + }; + + const { label, url } = attributes; let content; if ( isSelected ) { content = ( -
- setAttributes( { label } ) } - aria-label={ __( 'Navigation Label' ) } - maxRows={ 1 } - /> - <Dropdown - contentClassName="wp-block-navigation-menu-item__dropdown-content" - position="bottom left" - popoverProps={ POPOVER_PROPS } - renderToggle={ ( { isOpen, onToggle } ) => ( - <IconButton - icon={ isOpen ? 'arrow-up-alt2' : 'arrow-down-alt2' } - label={ __( 'More options' ) } - onClick={ onToggle } - aria-expanded={ isOpen } - /> - ) } - renderContent={ ( { onClose } ) => ( - <MenuItemActions - clientId={ clientId } - destination={ attributes.destination } - onEditLableClicked={ onEditLableClicked( onClose ) } - /> - ) } - /> - </div> + <TextControl + ref={ plainTextRef } + className="wp-block-navigation-menu-item__field" + value={ label } + onChange={ ( labelValue ) => setAttributes( { label: labelValue } ) } + label={ __( 'Navigation Label' ) } + hideLabelFromVision={ true } + /> ); } else { - content = attributes.label; + content = <div className="wp-block-navigation-menu-item__container"> + { label } + </div>; } + return ( <Fragment> + <BlockControls> + <Toolbar> + <ToolbarButton + name="link" + icon="admin-links" + title={ __( 'Link' ) } + onClick={ () => setIsLinkOpen( ! isLinkOpen ) } + /> + { isLinkOpen && + <> + <URLPopover + className="wp-block-navigation-menu-item__inline-link-input" + onClose={ closeURLPopover } + onFocusOutside={ onFocusOutside } + > + { ( ! url || isEditingLink ) && + <URLPopover.LinkEditor + value={ inputValue } + onChangeInputValue={ setUrlInput } + onKeyPress={ stopPropagation } + onKeyDown={ onKeyDown } + onSubmit={ ( event ) => event.preventDefault() } + autocompleteRef={ autocompleteRef } + /> + } + { ( url && ! isEditingLink ) && + <URLPopover.LinkViewer + onKeyPress={ stopPropagation } + url={ url } + /> + } + + </URLPopover> + </> + } + </Toolbar> + </BlockControls> <InspectorControls> <PanelBody title={ __( 'Menu Settings' ) } @@ -138,7 +185,12 @@ function NavigationMenuItemEdit( { /> </PanelBody> </InspectorControls> - <div className="wp-block-navigation-menu-item"> + <div className={ classnames( + 'wp-block-navigation-menu-item', { + 'is-editing': isSelected || isParentOfSelectedBlock, + 'is-selected': isSelected, + } ) } + > { content } { ( isSelected || isParentOfSelectedBlock ) && <InnerBlocks diff --git a/packages/block-library/src/navigation-menu-item/editor.scss b/packages/block-library/src/navigation-menu-item/editor.scss index 2d801052ba4107..8e6c06274129b1 100644 --- a/packages/block-library/src/navigation-menu-item/editor.scss +++ b/packages/block-library/src/navigation-menu-item/editor.scss @@ -1,39 +1,64 @@ -$menu-label-field-width: 140px; + +// Normalize menu items and edit containers, to look mostly the same. +.wp-block-navigation-menu-item__field .components-text-control__input.components-text-control__input, +.wp-block-navigation-menu-item__container { + border-radius: 0; + // Make it the same height as the appender to prevent a jump. Maybe revisit this. + line-height: $icon-button-size; + min-height: $icon-button-size; +} + +.wp-block-navigation-menu-item { + margin-right: $grid-size; + + // Provide a base menu item margin. + // This should be the same inside the field, + // and the edit container should compensate. + // This is to make sure the edit and view are the same. + padding: 0 $grid-size; +} .wp-block-navigation-menu-item__edit-container { - display: grid; - grid-auto-columns: min-content; - grid-auto-flow: column; - align-items: center; + display: flex; white-space: nowrap; - border: 1px solid $light-gray-500; - // two pixes comes from two times one pixel border - width: $menu-label-field-width + $icon-button-size + 2px; - padding-left: 1px; -} -.wp-block-navigation-menu-item__edit-container .wp-block-navigation-menu-item__field { - border-right: 1px solid $light-gray-500 !important; - width: $menu-label-field-width; - border: none; - border-radius: 0; - padding-left: $grid-size-large; + // Compensate for menu item base padding. + margin-left: -$grid-size; - min-height: $icon-button-size - 1px; - line-height: $icon-button-size - 1px; + .wp-block-navigation-menu-item__field { + margin-right: $grid-size; - &, - &:focus { - color: $dark-gray-500; + // This should match the padding of the menu item. + padding: 0 $grid-size; + + // This make it look like an input field. + // We may want to not style this at all, but let's try this. + // We don't use the mixins because they increase the size of the input, which doesn't work with PlainText. + box-shadow: inset 0 0 0 1px $dark-gray-200; + transition: box-shadow 0.1s linear; + border-radius: $radius-round-rectangle; + @include reduce-motion("transition"); + + &:focus { + color: $dark-gray-900; + box-shadow: inset 0 0 0 2px $blue-medium-focus; + + // Windows High Contrast mode will show this outline, but not the box-shadow. + outline: 2px solid transparent; + } } } - .wp-block-navigation-menu-item { font-family: $editor-font; - color: #0073af; font-weight: bold; font-size: $text-editor-font-size; + background-color: var(--background-color-menu-link); + color: var(--color-menu-link); + + &:focus { + color: var(--color-menu-link); + } } .wp-block-navigation-menu-item__nofollow-external-link { @@ -42,10 +67,7 @@ $menu-label-field-width: 140px; // Separator .wp-block-navigation-menu-item__separator { - margin-top: $grid-size; - margin-bottom: $grid-size; - margin-left: 0; - margin-right: 0; + margin: $grid-size 0 $grid-size; border-top: $border-width solid $light-gray-500; } @@ -60,10 +82,6 @@ $menu-label-field-width: 140px; } .wp-block-navigation-menu .block-editor-block-list__block[data-type="core/navigation-menu-item"] { - & > .block-editor-block-list__block-edit > div[role="toolbar"] { - display: none; - } - & > .block-editor-block-list__insertion-point { display: none; } diff --git a/packages/block-library/src/navigation-menu-item/index.js b/packages/block-library/src/navigation-menu-item/index.js index 496ef92070428e..9a51131a86b41a 100644 --- a/packages/block-library/src/navigation-menu-item/index.js +++ b/packages/block-library/src/navigation-menu-item/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; - +import { Path, SVG } from '@wordpress/components'; /** * Internal dependencies */ @@ -18,10 +18,12 @@ export const settings = { parent: [ 'core/navigation-menu' ], - icon: 'admin-links', + icon: <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24"><Path d="M12 7.27l4.28 10.43-3.47-1.53-.81-.36-.81.36-3.47 1.53L12 7.27M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71L12 2z" /></SVG>, description: __( 'Add a page, link, or other item to your Navigation Menu.' ), + __experimentalDisplayName: 'label', + edit, save, }; diff --git a/packages/block-library/src/navigation-menu-item/menu-item-actions.js b/packages/block-library/src/navigation-menu-item/menu-item-actions.js deleted file mode 100644 index 6e39036bab408a..00000000000000 --- a/packages/block-library/src/navigation-menu-item/menu-item-actions.js +++ /dev/null @@ -1,111 +0,0 @@ -/** - * WordPress dependencies - */ -import { - MenuItem, - NavigableMenu, -} from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { withDispatch } from '@wordpress/data'; -import { compose } from '@wordpress/compose'; - -function MenuItemActions( { - destination, - moveLeft, - moveRight, - moveToEnd, - moveToStart, - onEditLableClicked, - remove, -} ) { - return ( - <NavigableMenu> - <MenuItem - icon="admin-links" - > - { destination } - </MenuItem> - <MenuItem - onClick={ onEditLableClicked } - icon="edit" - > - { __( 'Edit label text' ) } - </MenuItem> - <div className="wp-block-navigation-menu-item__separator" /> - <MenuItem - onClick={ moveToStart } - icon="arrow-up-alt2" - > - { __( 'Move to start' ) } - </MenuItem> - <MenuItem - onClick={ moveLeft } - icon="arrow-left-alt2" - > - { __( 'Move left' ) } - </MenuItem> - <MenuItem - onClick={ moveRight } - icon="arrow-right-alt2" - > - { __( 'Move right' ) } - </MenuItem> - <MenuItem - onClick={ moveToEnd } - icon="arrow-down-alt2" - > - { __( 'Move to end' ) } - </MenuItem> - <MenuItem - icon="arrow-left-alt2" - > - { __( 'Nest underneath…' ) } - </MenuItem> - <div className="navigation-menu-item__separator" /> - <MenuItem - onClick={ remove } - icon="trash" - > - { __( 'Remove from menu' ) } - </MenuItem> - </NavigableMenu> - ); -} - -export default compose( [ - withDispatch( ( dispatch, { clientId }, { select } ) => { - const { - getBlockOrder, - getBlockRootClientId, - } = select( 'core/block-editor' ); - const parentID = getBlockRootClientId( clientId ); - const { - moveBlocksDown, - moveBlocksUp, - moveBlockToPosition, - removeBlocks, - } = dispatch( 'core/block-editor' ); - return { - moveToStart() { - moveBlockToPosition( clientId, parentID, parentID, 0 ); - }, - moveRight() { - moveBlocksDown( clientId, parentID ); - }, - moveLeft() { - moveBlocksUp( clientId, parentID ); - }, - moveToEnd() { - moveBlockToPosition( - clientId, - parentID, - parentID, - getBlockOrder( parentID ).length - 1 - ); - }, - remove() { - removeBlocks( clientId ); - }, - }; - } ), -] )( MenuItemActions ); diff --git a/packages/block-library/src/navigation-menu/block-colors-selector.js b/packages/block-library/src/navigation-menu/block-colors-selector.js new file mode 100644 index 00000000000000..0c1c98b691ef4f --- /dev/null +++ b/packages/block-library/src/navigation-menu/block-colors-selector.js @@ -0,0 +1,95 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { noop } from 'lodash'; + +/** + * WordPress dependencies + */ +import { IconButton, Dropdown, Toolbar } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { DOWN } from '@wordpress/keycodes'; +import { ColorPaletteControl, ContrastChecker } from '@wordpress/block-editor'; + +/** + * Color Selector Icon component. + * + * @return {*} React Icon component. + */ +const ColorSelectorIcon = ( { style } ) => + <div className="block-library-colors-selector__icon-container"> + <div + className="block-library-colors-selector__state-selection wp-block-navigation-menu-item" + style={ style } + > + Aa + </div> + </div>; + +/** + * Renders the Colors Selector Toolbar with the icon button. + * + * @param {Object} style - Colors style object. + * @return {*} React toggle button component. + */ +const renderToggleComponent = ( style ) => ( { onToggle, isOpen } ) => { + const openOnArrowDown = ( event ) => { + if ( ! isOpen && event.keyCode === DOWN ) { + event.preventDefault(); + event.stopPropagation(); + onToggle(); + } + }; + + return ( + <Toolbar> + <IconButton + className="components-icon-button components-toolbar__control block-library-colors-selector__toggle" + label={ __( 'Open Colors Selector' ) } + onClick={ onToggle } + onKeyDown={ openOnArrowDown } + icon={ <ColorSelectorIcon style={ style } /> } + /> + </Toolbar> + ); +}; + +const renderContent = ( { backgroundColor, textColor, onColorChange = noop } ) => ( () => { + const setColor = ( attr ) => ( value ) => onColorChange( { attr, value } ); + + return ( + <> + <div className="color-palette-controller-container"> + <ColorPaletteControl + value={ backgroundColor.color } + onChange={ setColor( 'backgroundColor' ) } + label={ __( 'Background Color' ) } + /> + </div> + + <div className="color-palette-controller-container"> + <ColorPaletteControl + value={ textColor.color } + onChange={ setColor( 'textColor' ) } + label={ __( 'Text Color' ) } + /> + </div> + + <ContrastChecker + textColor={ textColor.color } + backgroundColor={ backgroundColor.color } + isLargeText={ false } + /> + </> + ); +} ); + +export default ( { style, className, ...colorControlProps } ) => + <Dropdown + position="bottom right" + className={ classnames( 'block-library-colors-selector', className ) } + contentClassName="block-library-colors-selector__popover" + renderToggle={ renderToggleComponent( style ) } + renderContent={ renderContent( colorControlProps ) } + />; diff --git a/packages/block-library/src/navigation-menu/edit.js b/packages/block-library/src/navigation-menu/edit.js index 5b4ad5ce475f9a..fdb6fd7c2fa4db 100644 --- a/packages/block-library/src/navigation-menu/edit.js +++ b/packages/block-library/src/navigation-menu/edit.js @@ -1,14 +1,21 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ import { Fragment, useMemo, + useEffect, } from '@wordpress/element'; import { InnerBlocks, InspectorControls, BlockControls, + withColors, } from '@wordpress/block-editor'; import { withSelect } from '@wordpress/data'; import { @@ -17,19 +24,26 @@ import { Spinner, Toolbar, } from '@wordpress/components'; +import { compose } from '@wordpress/compose'; + import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import useBlockNavigator from './use-block-navigator'; +import BlockColorsStyleSelector from './block-colors-selector'; function NavigationMenu( { attributes, - setAttributes, clientId, pages, isRequesting, + backgroundColor, + textColor, + setBackgroundColor, + setTextColor, + setAttributes, } ) { const { navigatorToolbarButton, navigatorModal } = useBlockNavigator( clientId ); const defaultMenuItems = useMemo( @@ -39,19 +53,74 @@ function NavigationMenu( { } return pages.map( ( page ) => { return [ 'core/navigation-menu-item', - { label: page.title.rendered, destination: page.permalink_template }, + { label: page.title.rendered, url: page.permalink_template }, ]; } ); }, [ pages ] ); + const navigationMenuStyles = {}; + if ( textColor.color ) { + navigationMenuStyles[ '--color-menu-link' ] = textColor.color; + } + + if ( backgroundColor.color ) { + navigationMenuStyles[ '--background-color-menu-link' ] = backgroundColor.color; + } + + const navigationMenuClasses = classnames( + 'wp-block-navigation-menu', { + 'has-text-color': textColor.color, + 'has-background-color': backgroundColor.color, + } + ); + + /** + * Set the color type according to the given values. + * It propagate the color values into the attributes object. + * Both `backgroundColorValue` and `textColorValue` are + * using the inline styles. + * + * @param {Object} colorsData Arguments passed by BlockColorsStyleSelector onColorChange. + * @param {string} colorsData.attr Color attribute. + * @param {boolean} colorsData.value Color attribute value. + */ + const setColorType = ( { attr, value } ) => { + switch ( attr ) { + case 'backgroundColor': + setBackgroundColor( value ); + setAttributes( { backgroundColorValue: value } ); + break; + + case 'textColor': + setTextColor( value ); + setAttributes( { textColorValue: value } ); + break; + } + }; + + useEffect( () => { + // Set/Unset colors CSS classes. + setAttributes( { + backgroundColorCSSClass: backgroundColor.class ? backgroundColor.class : null, + textColorCSSClass: textColor.class ? textColor.class : null, + } ); + }, [ backgroundColor.class, textColor.class ] ); + return ( <Fragment> <BlockControls> <Toolbar> { navigatorToolbarButton } </Toolbar> + <BlockColorsStyleSelector + style={ navigationMenuStyles } + className={ navigationMenuClasses } + backgroundColor={ backgroundColor } + textColor={ textColor } + onColorChange={ setColorType } + /> </BlockControls> { navigatorModal } <InspectorControls> @@ -60,23 +129,21 @@ function NavigationMenu( { > <CheckboxControl value={ attributes.automaticallyAdd } - onChange={ ( automaticallyAdd ) => { - setAttributes( { automaticallyAdd } ); - } } + onChange={ ( automaticallyAdd ) => setAttributes( { automaticallyAdd } ) } label={ __( 'Automatically add new pages' ) } help={ __( 'Automatically add new top level pages to this menu.' ) } /> </PanelBody> </InspectorControls> - <div className="wp-block-navigation-menu"> - { isRequesting && - <Spinner /> - } + + <div className={ navigationMenuClasses } style={ navigationMenuStyles }> + { isRequesting && <Spinner /> } { pages && <InnerBlocks template={ defaultMenuItems ? defaultMenuItems : null } allowedBlocks={ [ 'core/navigation-menu-item' ] } templateInsertUpdatesSelection={ false } + __experimentalMoverDirection={ 'horizontal' } /> } </div> @@ -84,17 +151,19 @@ function NavigationMenu( { ); } -export default withSelect( ( select ) => { - const { getEntityRecords } = select( 'core' ); - const { isResolving } = select( 'core/data' ); - const filterDefaultPages = { - parent: 0, - order: 'asc', - orderby: 'id', - }; - return { - pages: getEntityRecords( 'postType', 'page', filterDefaultPages ), - isRequesting: isResolving( 'core', 'getEntityRecords', [ 'postType', 'page', filterDefaultPages ] ), - }; -} )( NavigationMenu ); - +export default compose( [ + withColors( { backgroundColor: 'background-color', textColor: 'color' } ), + withSelect( ( select ) => { + const { getEntityRecords } = select( 'core' ); + const { isResolving } = select( 'core/data' ); + const filterDefaultPages = { + parent: 0, + order: 'asc', + orderby: 'id', + }; + return { + pages: getEntityRecords( 'postType', 'page', filterDefaultPages ), + isRequesting: isResolving( 'core', 'getEntityRecords', [ 'postType', 'page', filterDefaultPages ] ), + }; + } ), +] )( NavigationMenu ); diff --git a/packages/block-library/src/navigation-menu/editor.scss b/packages/block-library/src/navigation-menu/editor.scss index 2238b2fae8dd7c..cc663e7787a998 100644 --- a/packages/block-library/src/navigation-menu/editor.scss +++ b/packages/block-library/src/navigation-menu/editor.scss @@ -1,9 +1,63 @@ +// Reduce the paddings, margins, and UI of inner-blocks. +// @todo: eventually we may add a feature that lets a parent container absorb the block UI of a child block. +// When that happens, leverage that instead of the following overrides. +[data-type="core/navigation-menu"] { + // 1. Reset margins on immediate innerblocks container. + .wp-block-navigation-menu .block-editor-inner-blocks > .block-editor-block-list__layout { + margin-left: 0; + margin-right: 0; + } + + // 2. Remove paddings on subsequent immediate children. + .wp-block-navigation-menu .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block { + width: auto; + padding-left: 0; + padding-right: 0; + margin-left: 0; // something + } + + // 3. Remove margins on subsequent Edit container. + .wp-block-navigation-menu .block-editor-inner-blocks > .block-editor-block-list__layout > .wp-block > .block-editor-block-list__block-edit { + margin-left: 0; + margin-right: 0; + } + + // 4. Remove vertical margins on subsequent block container. + .wp-block-navigation-menu .block-editor-inner-blocks > .block-editor-block-list__layout .wp-block > .block-editor-block-list__block-edit > [data-block] { + margin-top: 0; + margin-bottom: 0; + } + + // Remove the dashed outlines for child blocks. + &.is-hovered .wp-block-navigation-menu .block-editor-block-list__block-edit::before, + &.is-selected .wp-block-navigation-menu .block-editor-block-list__block-edit::before, + &.has-child-selected .wp-block-navigation-menu .block-editor-block-list__block-edit::before { + border-color: transparent !important; // !important used to keep the selector from growing any more complex. + } + + // Hide the breadcrumb. + // Hide the sibling inserter. + .wp-block-navigation-menu .block-editor-block-list__insertion-point, + .wp-block-navigation-menu .block-editor-block-list__breadcrumb { + display: none; + } + + // Polish the Appender. + .wp-block-navigation-menu .block-list-appender { + margin: 0; + + .block-editor-button-block-appender { + padding: $grid-size; + outline: none; + background: none; + } + } +} + + .wp-block-navigation-menu .block-editor-block-list__layout, .wp-block-navigation-menu { display: flex; - grid-auto-columns: min-content; - grid-auto-flow: column; - align-items: top; white-space: nowrap; } @@ -12,7 +66,99 @@ } .wp-block-navigation-menu-item { - .wp-block-navigation-menu-item { + margin-left: $grid-size; + .wp-block-navigation-menu-item__link { margin-left: $grid-size-large; } } + +/** + * Colors Selector component + */ +$colors-selector-size: 22px; +.block-library-colors-selector { + width: auto; + + // Toolbar colors-selector button. + .block-library-colors-selector__toggle { + display: block; + margin: 0 auto; + padding: 3px; + width: auto; + } + + // Button container. + .block-library-colors-selector__icon-container { + width: 42px; + height: 30px; + position: relative; + margin: 0 auto; + padding: 3px; + display: flex; + align-items: center; + border-radius: 4px; + + // Add the button arrow. + &::after { + @include dropdown-arrow(); + } + + // Styling button states. + &:focus, + &:hover { + color: $dark-gray-500; + box-shadow: inset 0 0 0 1px $dark-gray-500, inset 0 0 0 2px #fff; + } + } + + // colors-selector - selection status. + .block-library-colors-selector__state-selection { + font-size: 11px; + font-style: normal; + font-family: inherit; + font-weight: 600; + + border-radius: $colors-selector-size / 2; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); + + width: $colors-selector-size; + min-width: $colors-selector-size; + height: $colors-selector-size; + min-height: $colors-selector-size; + line-height: ($colors-selector-size - 2); + + background-color: var(--background-color-menu-link); + color: var(--color-menu-link); + } + + &:not(.has-background-color) .block-library-colors-selector__state-selection { + background-image: linear-gradient(135deg, rgba(0, 0, 0, 0.08) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, 0.08) 50%, rgba(0, 0, 0, 0.08) 75%, transparent 75%, transparent 100%); + background-size: 12px 12px; + } +} + +// Colors Selector Popover. +$color-control-label-height: 20px; +.block-library-colors-selector__popover { + .color-palette-controller-container { + padding: 16px; + } + + .components-base-control__label { + height: $color-control-label-height; + line-height: $color-control-label-height; + } + + .component-color-indicator { + float: right; + margin-top: 2px; + } +} + +.block-editor-block-mover { + &.is-horizontal { + .block-editor-block-mover__control-drag-handle { + display: none; + } + } +} diff --git a/packages/block-library/src/navigation-menu/index.php b/packages/block-library/src/navigation-menu/index.php index ba6348bd66e1b6..b49a41a06aa11c 100644 --- a/packages/block-library/src/navigation-menu/index.php +++ b/packages/block-library/src/navigation-menu/index.php @@ -5,6 +5,56 @@ * @package gutenberg */ +/** + * Build an array with CSS classes and inline styles defining the colors + * which will be applied to the navigation menu markup in the front-end. + * + * @param array $attributes NavigationMenu block attributes. + * @return array Colors CSS classes and inline styles. + */ +function build_css_colors( $attributes ) { + // CSS classes. + $colors = array( + 'bg_css_classes' => '', + 'bg_inline_styles' => '', + 'text_css_classes' => '', + 'text_inline_styles' => '', + ); + + // Background color. + // Background color - has text color. + if ( array_key_exists( 'backgroundColor', $attributes ) ) { + $colors['bg_css_classes'] .= ' has-background-color'; + } + + // Background color - add custom CSS class. + if ( array_key_exists( 'backgroundColorCSSClass', $attributes ) ) { + $colors['bg_css_classes'] .= " {$attributes['backgroundColorCSSClass']}"; + + } elseif ( array_key_exists( 'customBackgroundColor', $attributes ) ) { + // Background color - or add inline `background-color` style. + $colors['bg_inline_styles'] = ' style="background-color: ' . esc_attr( $attributes['customBackgroundColor'] ) . ';"'; + } + + // Text color. + // Text color - has text color. + if ( array_key_exists( 'textColor', $attributes ) ) { + $colors['text_css_classes'] .= ' has-text-color'; + } + // Text color - add custom CSS class. + if ( array_key_exists( 'textColorCSSClass', $attributes ) ) { + $colors['text_css_classes'] .= " {$attributes['textColorCSSClass']}"; + + } elseif ( array_key_exists( 'customTextColor', $attributes ) ) { + // Text color - or add inline `color` style. + $colors['text_inline_styles'] = ' style="color: ' . esc_attr( $attributes['customTextColor'] ) . ';"'; + } + + $colors['bg_css_classes'] = esc_attr( trim( $colors['bg_css_classes'] ) ); + $colors['text_css_classes'] = esc_attr( trim( $colors['text_css_classes'] ) ); + + return $colors; +} /** * Renders the `core/navigation-menu` block on server. * @@ -15,22 +65,45 @@ * @return string Returns the post content with the legacy widget added. */ function render_block_navigation_menu( $attributes, $content, $block ) { - return '<nav class="wp-block-navigation-menu">' . build_navigation_menu_html( $block ) . '</nav>'; + // Inline computed colors. + $comp_inline_styles = ''; + if ( array_key_exists( 'backgroundColorValue', $attributes ) ) { + $comp_inline_styles .= ' background-color: ' . esc_attr( $attributes['backgroundColorValue'] ) . ';'; + } + + if ( array_key_exists( 'textColorValue', $attributes ) ) { + $comp_inline_styles .= ' color: ' . esc_attr( $attributes['textColorValue'] ) . ';'; + } + $comp_inline_styles = ! empty( $comp_inline_styles ) + ? ' style="' . esc_attr( trim( $comp_inline_styles ) ) . '"' + : ''; + + $colors = build_css_colors( $attributes ); + + return "<nav class='wp-block-navigation-menu' {$comp_inline_styles}>" . + build_navigation_menu_html( $block, $colors ) . + '</nav>'; } /** * Walks the inner block structure and returns an HTML list for it. * - * @param array $block The block. + * @param array $block The block. + * @param array $colors Contains inline styles and CSS classes to apply to menu item. * * @return string Returns an HTML list from innerBlocks. */ -function build_navigation_menu_html( $block ) { +function build_navigation_menu_html( $block, $colors ) { $html = ''; foreach ( (array) $block['innerBlocks'] as $key => $menu_item ) { - $html .= '<li class="wp-block-navigation-menu-item"><a class="wp-block-navigation-menu-item"'; - if ( isset( $menu_item['attrs']['destination'] ) ) { - $html .= ' href="' . $menu_item['attrs']['destination'] . '"'; + + $html .= '<li class="wp-block-navigation-menu-item ' . $colors['bg_css_classes'] . '"' . $colors['bg_inline_styles'] . '>' . + '<a + class="wp-block-navigation-menu-item__link ' . $colors['text_css_classes'] . '" + ' . $colors['text_inline_styles']; + + if ( isset( $menu_item['attrs']['url'] ) ) { + $html .= ' href="' . $menu_item['attrs']['url'] . '"'; } if ( isset( $menu_item['attrs']['title'] ) ) { $html .= ' title="' . $menu_item['attrs']['title'] . '"'; @@ -42,7 +115,7 @@ function build_navigation_menu_html( $block ) { $html .= '</a>'; if ( count( (array) $menu_item['innerBlocks'] ) > 0 ) { - $html .= build_navigation_menu_html( $menu_item ); + $html .= build_navigation_menu_html( $menu_item, $colors ); } $html .= '</li>'; @@ -54,18 +127,57 @@ function build_navigation_menu_html( $block ) { * Register the navigation menu block. * * @uses render_block_navigation_menu() + * @throws WP_Error An WP_Error exception parsing the block definition. */ function register_block_core_navigation_menu() { + register_block_type( 'core/navigation-menu', array( 'category' => 'layout', 'attributes' => array( - 'automaticallyAdd' => array( + 'className' => array( + 'type' => 'string', + ), + + 'automaticallyAdd' => array( 'type' => 'boolean', 'default' => false, ), + + 'backgroundColor' => array( + 'type' => 'string', + ), + + 'textColor' => array( + 'type' => 'string', + ), + + 'backgroundColorValue' => array( + 'type' => 'string', + ), + + 'textColorValue' => array( + 'type' => 'string', + ), + + 'customBackgroundColor' => array( + 'type' => 'string', + ), + + 'customTextColor' => array( + 'type' => 'string', + ), + + 'backgroundColorCSSClass' => array( + 'type' => 'string', + ), + + 'textColorCSSClass' => array( + 'type' => 'string', + ), ), + 'render_callback' => 'render_block_navigation_menu', ) ); diff --git a/packages/block-library/src/navigation-menu/style.scss b/packages/block-library/src/navigation-menu/style.scss new file mode 100644 index 00000000000000..3c2222ef59f252 --- /dev/null +++ b/packages/block-library/src/navigation-menu/style.scss @@ -0,0 +1,119 @@ +.wp-block-navigation-menu { + + & > ul { + display: block; + list-style: none; + margin: 0; + max-width: none; + padding-left: 0; + position: relative; + + @include break-small { + display: flex; + flex-wrap: wrap; + } + + ul { + padding-left: 0; + } + + li { + position: relative; + z-index: 1; + + &:hover, + &:focus-within { + cursor: pointer; + z-index: 99999; + } + + // Submenu Display + &:hover > ul, + &:focus-within > ul, + & ul:hover, + & ul:focus { + visibility: visible; + opacity: 1; + display: block; + } + } + + & > li { + + & > a { + padding-left: 0; + + @include break-small { + padding-left: 16px; + } + } + + &:first-of-type > a { + padding-left: 0; + } + + &:last-of-type > a { + padding-right: 0; + } + } + + // Sub-menus Flyout + & > li > ul { + margin: 0; + position: absolute; + background: #fff; + box-shadow: 0 0 8px 2px rgba(0, 0, 0, 0.2); + left: 0; + top: 100%; + min-width: max-content; + opacity: 0; + transition: all 0.5s ease; + visibility: hidden; + + ul { + width: 100%; + } + } + } + + // Menu Link + a { + display: block; + padding: 16px; + } + + // Sub-menu depth indicators + ul ul { + + list-style: none; + margin-left: 0; + // Reset the counter for each UL + counter-reset: nested-list; + + li a { + + padding-top: 8px; + padding-bottom: 8px; + + &::before { + // Increment the dashes + counter-increment: nested-list; + // Insert dashes with spaces in between + content: "\2013\00a0" counters(nested-list, "\2013\00a0", none); + } + } + } + + // Top-level sub-menu indicators + & .has-sub-menu > a { + + &::after { + content: "\00a0\25BC"; + display: inline-block; + font-size: 0.6rem; + height: inherit; + width: inherit; + } + } + +} diff --git a/packages/block-library/src/site-title/block.json b/packages/block-library/src/site-title/block.json new file mode 100644 index 00000000000000..a853cde2517b51 --- /dev/null +++ b/packages/block-library/src/site-title/block.json @@ -0,0 +1,4 @@ +{ + "name": "core/site-title", + "category": "layout" +} diff --git a/packages/block-library/src/site-title/edit.js b/packages/block-library/src/site-title/edit.js new file mode 100644 index 00000000000000..0294fb82d17a18 --- /dev/null +++ b/packages/block-library/src/site-title/edit.js @@ -0,0 +1,39 @@ +/** + * WordPress dependencies + */ +import { + useEntityProp, + __experimentalUseEntitySaving, +} from '@wordpress/core-data'; +import { Button } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { RichText } from '@wordpress/block-editor'; + +export default function SiteTitleEdit() { + const [ title, setTitle ] = useEntityProp( 'root', 'site', 'title' ); + const [ isDirty, isSaving, save ] = __experimentalUseEntitySaving( + 'root', + 'site', + 'title' + ); + return ( + <> + <Button + isPrimary + className="wp-block-site-title__save-button" + disabled={ ! isDirty || ! title } + isBusy={ isSaving } + onClick={ save } + > + { __( 'Update' ) } + </Button> + <RichText + tagName="h1" + placeholder={ __( 'Site Title' ) } + value={ title } + onChange={ setTitle } + allowedFormats={ [] } + /> + </> + ); +} diff --git a/packages/block-library/src/site-title/editor.scss b/packages/block-library/src/site-title/editor.scss new file mode 100644 index 00000000000000..f2b8359cf3790b --- /dev/null +++ b/packages/block-library/src/site-title/editor.scss @@ -0,0 +1,6 @@ +.wp-block-site-title__save-button { + position: absolute; + right: 0; + top: 0; + z-index: z-index(".wp-block-site-title__save-button"); +} diff --git a/packages/block-library/src/site-title/icon.js b/packages/block-library/src/site-title/icon.js new file mode 100644 index 00000000000000..1ab74dec2c32d3 --- /dev/null +++ b/packages/block-library/src/site-title/icon.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path, Circle } from '@wordpress/components'; + +export default ( + <SVG xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <Path fill="none" d="M0 0h24v24H0V0z" /> + <Path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zM7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 2.88-2.88 7.19-5 9.88C9.92 16.21 7 11.85 7 9z" /> + <Circle cx="12" cy="9" r="2.5" /> + </SVG> +); diff --git a/packages/block-library/src/site-title/index.js b/packages/block-library/src/site-title/index.js new file mode 100644 index 00000000000000..4b8fc50b86b34f --- /dev/null +++ b/packages/block-library/src/site-title/index.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import icon from './icon'; +import edit from './edit'; + +const { name } = metadata; +export { metadata, name }; + +export const settings = { + title: __( 'Site Title' ), + icon, + edit, +}; diff --git a/packages/block-library/src/site-title/index.php b/packages/block-library/src/site-title/index.php new file mode 100644 index 00000000000000..6e5b5786436f09 --- /dev/null +++ b/packages/block-library/src/site-title/index.php @@ -0,0 +1,28 @@ +<?php +/** + * Server-side rendering of the `core/site-title` block. + * + * @package WordPress + */ + +/** + * Renders the `core/site-title` block on the server. + * + * @return string The render. + */ +function render_block_core_site_title() { + return sprintf( '<h1>%s</h1>', get_bloginfo( 'name' ) ); +} + +/** + * Registers the `core/site-title` block on the server. + */ +function register_block_core_site_title() { + register_block_type( + 'core/site-title', + array( + 'render_callback' => 'render_block_core_site_title', + ) + ); +} +add_action( 'init', 'register_block_core_site_title' ); diff --git a/packages/block-library/src/social-link/index.php b/packages/block-library/src/social-link/index.php index 4d04c9cd1c12c4..86d83b685e21e8 100644 --- a/packages/block-library/src/social-link/index.php +++ b/packages/block-library/src/social-link/index.php @@ -22,7 +22,7 @@ function render_core_social_link( $attributes ) { } $icon = core_social_link_get_icon( $site ); - return '<li class="wp-social-link wp-social-link-' . $site . '"><a href="' . esc_attr( $url ) . '"> ' . $icon . '</a></li>'; + return '<li class="wp-social-link wp-social-link-' . $site . '"><a href="' . esc_url( $url ) . '"> ' . $icon . '</a></li>'; } /** diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index 433ef8cb3aa2f3..3fd7e3c2b8e041 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -11,6 +11,7 @@ @import "./latest-comments/style.scss"; @import "./latest-posts/style.scss"; @import "./media-text/style.scss"; +@import "./navigation-menu/style.scss"; @import "./paragraph/style.scss"; @import "./pullquote/style.scss"; @import "./quote/style.scss"; @@ -129,6 +130,82 @@ .has-very-dark-gray-color { color: #313131; } + + // Gradients + // Our classes uses the same values we set for gradient value attributes, and we can not use spacing because of WP multi site kses rule. + /* stylelint-disable function-comma-space-after */ + .has-vivid-cyan-blue-to-vivid-purple-gradient-background { + background: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%); + } + + .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background { + background: linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%); + } + + .has-light-green-cyan-to-vivid-green-cyan-gradient-background { + background: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%); + } + + .has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background { + background: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%); + } + + .has-luminous-vivid-orange-to-vivid-red-gradient-background { + background: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%); + } + + .has-very-light-gray-to-cyan-bluish-gray-gradient-background { + background: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%); + } + + .has-cool-to-warm-spectrum-gradient-background { + background: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%); + } + + .has-blush-light-purple-gradient-background { + background: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%); + } + + .has-blush-bordeaux-gradient-background { + background: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%); + } + + .has-purple-crush-gradient-background { + background: linear-gradient(135deg,rgb(52,226,228) 0%,rgb(71,33,251) 50%,rgb(171,29,254) 100%); + } + + .has-luminous-dusk-gradient-background { + background: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%); + } + + .has-hazy-dawn-gradient-background { + background: linear-gradient(135deg,rgb(250,172,168) 0%,rgb(218,208,236) 100%); + } + + .has-pale-ocean-gradient-background { + background: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%); + } + + .has-electric-grass-gradient-background { + background: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%); + } + + .has-subdued-olive-gradient-background { + background: linear-gradient(135deg,rgb(250,250,225) 0%,rgb(103,166,113) 100%); + } + + .has-atomic-cream-gradient-background { + background: linear-gradient(135deg,rgb(253,215,154) 0%,rgb(0,74,89) 100%); + } + + .has-nightshade-gradient-background { + background: linear-gradient(135deg,rgb(51,9,104) 0%,rgb(49,205,207) 100%); + } + + .has-midnight-gradient-background { + background: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%); + } + /* stylelint-enable function-comma-space-after */ } diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js index b86c0e282d7f9b..1599bb7e53f64f 100644 --- a/packages/block-library/src/table/edit.js +++ b/packages/block-library/src/table/edit.js @@ -40,7 +40,6 @@ import { deleteColumn, toggleSection, isEmptyTableSection, - isCellSelected, } from './state'; import icon from './icon'; @@ -435,7 +434,6 @@ export class TableEdit extends Component { } const Tag = `t${ name }`; - const { selectedCell } = this.state; return ( <Tag> @@ -447,35 +445,21 @@ export class TableEdit extends Component { rowIndex, columnIndex, }; - const isSelected = isCellSelected( cellLocation, selectedCell ); const cellClasses = classnames( { - 'is-selected': isSelected, [ `has-text-align-${ align }` ]: align, - } ); - const richTextClassName = 'wp-block-table__cell-content'; + }, 'wp-block-table__cell-content' ); return ( - <CellTag + <RichText + tagName={ CellTag } key={ columnIndex } className={ cellClasses } scope={ CellTag === 'th' ? scope : undefined } - onClick={ ( event ) => { - // When a cell is selected, forward focus to the child RichText. This solves an issue where the - // user may click inside a cell, but outside of the RichText, resulting in nothing happening. - const richTextElement = event && event.target && event.target.querySelector( `.${ richTextClassName }` ); - if ( richTextElement ) { - richTextElement.focus(); - } - } } - > - <RichText - className={ richTextClassName } - value={ content } - onChange={ this.onChange } - unstableOnFocus={ this.createOnFocus( cellLocation ) } - /> - </CellTag> + value={ content } + onChange={ this.onChange } + unstableOnFocus={ this.createOnFocus( cellLocation ) } + /> ); } ) } </tr> diff --git a/packages/block-library/src/table/editor.scss b/packages/block-library/src/table/editor.scss index f5dbb5f2643c04..16002aa4dbaae1 100644 --- a/packages/block-library/src/table/editor.scss +++ b/packages/block-library/src/table/editor.scss @@ -36,7 +36,6 @@ td, th { - padding: 0; border: $border-width solid; } @@ -47,9 +46,6 @@ border-style: double; } - &__cell-content { - padding: 0.5em; - } // Extra specificity to override default Placeholder styles. &__placeholder-form.wp-block-table__placeholder-form { text-align: left; diff --git a/packages/block-library/src/video/edit.js b/packages/block-library/src/video/edit.js index 6236e51a95c935..d7b3cad0592602 100644 --- a/packages/block-library/src/video/edit.js +++ b/packages/block-library/src/video/edit.js @@ -319,10 +319,8 @@ class VideoEdit extends Component { export default compose( [ withSelect( ( select ) => { const { getSettings } = select( 'core/block-editor' ); - const { __experimentalMediaUpload } = getSettings(); - return { - mediaUpload: __experimentalMediaUpload, - }; + const { mediaUpload } = getSettings(); + return { mediaUpload }; } ), withNotices, withInstanceId, diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index 924dc7e4ad552e..8f2695921aa110 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-default-parser", - "version": "3.4.0", + "version": "3.4.1", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 28b1a0436de041..289e8b8e86afd4 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-spec-parser", - "version": "3.3.0", + "version": "3.3.1", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/README.md b/packages/blocks/README.md index 1af3f49adef88a..97a4e95676fc28 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -768,7 +768,7 @@ wrapped component. _Returns_ -- `Component`: Enhanced component with injected BlockContent as prop. +- `WPComponent`: Enhanced component with injected BlockContent as prop. <!-- END TOKEN(Autogenerated API docs) --> diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 3456e2386251e0..30f3255b9188b7 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "6.7.0", + "version": "6.7.2", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/src/api/factory.js b/packages/blocks/src/api/factory.js index fae2e0e4da9c06..e793800ecb1362 100644 --- a/packages/blocks/src/api/factory.js +++ b/packages/blocks/src/api/factory.js @@ -44,26 +44,26 @@ export function createBlock( name, attributes = {}, innerBlocks = [] ) { // Ensure attributes contains only values defined by block type, and merge // default values for missing attributes. - const sanitizedAttributes = reduce( blockType.attributes, ( result, schema, key ) => { + const sanitizedAttributes = reduce( blockType.attributes, ( accumulator, schema, key ) => { const value = attributes[ key ]; if ( undefined !== value ) { - result[ key ] = value; + accumulator[ key ] = value; } else if ( schema.hasOwnProperty( 'default' ) ) { - result[ key ] = schema.default; + accumulator[ key ] = schema.default; } if ( [ 'node', 'children' ].indexOf( schema.source ) !== -1 ) { // Ensure value passed is always an array, which we're expecting in // the RichText component to handle the deprecated value. - if ( typeof result[ key ] === 'string' ) { - result[ key ] = [ result[ key ] ]; - } else if ( ! Array.isArray( result[ key ] ) ) { - result[ key ] = []; + if ( typeof accumulator[ key ] === 'string' ) { + accumulator[ key ] = [ accumulator[ key ] ]; + } else if ( ! Array.isArray( accumulator[ key ] ) ) { + accumulator[ key ] = []; } } - return result; + return accumulator; }, {} ); const clientId = uuid(); diff --git a/packages/blocks/src/api/index.native.js b/packages/blocks/src/api/index.native.js deleted file mode 100644 index b2cfd4963d2e81..00000000000000 --- a/packages/blocks/src/api/index.native.js +++ /dev/null @@ -1,44 +0,0 @@ -export { - cloneBlock, - createBlock, - switchToBlockType, -} from './factory'; -export { - default as parse, - getBlockAttributes, - parseWithAttributeSchema, -} from './parser'; -export { - default as serialize, - getBlockContent, - getBlockDefaultClassName, - getSaveContent, -} from './serializer'; -export { - registerBlockType, - unregisterBlockType, - getFreeformContentHandlerName, - setUnregisteredTypeHandlerName, - getUnregisteredTypeHandlerName, - getBlockType, - getBlockTypes, - getBlockSupport, - hasBlockSupport, - isReusableBlock, - getChildBlockNames, - hasChildBlocks, - hasChildBlocksWithInserterSupport, - setDefaultBlockName, - getDefaultBlockName, - setGroupingBlockName, -} from './registration'; -export { - isUnmodifiedDefaultBlock, - normalizeIconObject, -} from './utils'; -export { - doBlocksMatchTemplate, - synchronizeBlocksWithTemplate, -} from './templates'; -export { pasteHandler, getPhrasingContentSchema } from './raw-handling'; -export { default as children } from './children'; diff --git a/packages/blocks/src/api/parser.js b/packages/blocks/src/api/parser.js index 89b4f5ee5cc9f9..6300eac329f153 100644 --- a/packages/blocks/src/api/parser.js +++ b/packages/blocks/src/api/parser.js @@ -565,12 +565,12 @@ export function serializeBlockNode( blockNode, options = {} ) { * @return {Function} An implementation which parses the post content. */ const createParse = ( parseImplementation ) => - ( content ) => parseImplementation( content ).reduce( ( memo, blockNode ) => { + ( content ) => parseImplementation( content ).reduce( ( accumulator, blockNode ) => { const block = createBlockWithFallback( blockNode ); if ( block ) { - memo.push( block ); + accumulator.push( block ); } - return memo; + return accumulator; }, [] ); /** diff --git a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js index 5c5ef223298fe6..4f3c3a2a810a99 100644 --- a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js +++ b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { includes } from 'lodash'; + /** * WordPress dependencies */ @@ -11,6 +16,7 @@ export default function( node, doc ) { fontWeight, fontStyle, textDecorationLine, + textDecoration, verticalAlign, } = node.style; @@ -22,7 +28,10 @@ export default function( node, doc ) { wrap( doc.createElement( 'em' ), node ); } - if ( textDecorationLine === 'line-through' ) { + // Some DOM implementations (Safari, JSDom) don't support + // style.textDecorationLine, so we check style.textDecoration as a + // fallback. + if ( textDecorationLine === 'line-through' || includes( textDecoration, 'line-through' ) ) { wrap( doc.createElement( 's' ), node ); } diff --git a/packages/blocks/src/api/raw-handling/shortcode-converter.js b/packages/blocks/src/api/raw-handling/shortcode-converter.js index 5385f0aed41502..10f1159c92cb0c 100644 --- a/packages/blocks/src/api/raw-handling/shortcode-converter.js +++ b/packages/blocks/src/api/raw-handling/shortcode-converter.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { some, castArray, first, mapValues, pickBy, includes } from 'lodash'; +import { some, castArray, find, mapValues, pickBy, includes } from 'lodash'; /** * WordPress dependencies @@ -29,7 +29,7 @@ function segmentHTMLToShortcodeBlock( HTML, lastIndex = 0 ) { } const transformTags = castArray( transformation.tag ); - const transformTag = first( transformTags ); + const transformTag = find( transformTags, ( tag ) => regexp( tag ).test( HTML ) ); let match; diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index 024f9e9d55a3e3..0d3dd7827680ec 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -60,11 +60,11 @@ import { DEPRECATED_ENTRY_KEYS } from './constants'; /** * Defined behavior of a block type. * - * @typedef {Object} WPBlockType + * @typedef {Object} WPBlock * - * @property {string} name Block type's namespaced name. - * @property {string} title Human-readable block type label. - * @property {string} category Block type category classification, + * @property {string} name Block type's namespaced name. + * @property {string} title Human-readable block type label. + * @property {string} category Block type category classification, * used in search interfaces to arrange * block types by category. * @property {WPBlockTypeIcon} [icon] Block type icon. @@ -74,7 +74,7 @@ import { DEPRECATED_ENTRY_KEYS } from './constants'; * @property {WPComponent} [save] Optional component describing * serialized markup structure of a * block type. - * @property {WPComponent} edit Component rendering an element to + * @property {WPComponent} edit Component rendering an element to * manipulate the attributes of a block * in the context of an editor. */ @@ -114,7 +114,7 @@ export function unstable__bootstrapServerSideBlockDefinitions( definitions ) { / * @param {Object} settings Block settings. * * @return {?WPBlock} The block, if it has been successfully registered; - * otherwise `undefined`. + * otherwise `undefined`. */ export function registerBlockType( name, settings ) { settings = { @@ -234,7 +234,7 @@ export function registerBlockType( name, settings ) { * @param {string} name Block name. * * @return {?WPBlock} The previous block value, if it has been successfully - * unregistered; otherwise `undefined`. + * unregistered; otherwise `undefined`. */ export function unregisterBlockType( name ) { const oldBlock = select( 'core/blocks' ).getBlockType( name ); diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 1776a32e362999..d2dfbcab2d672b 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -85,9 +85,9 @@ export function getSaveElement( blockTypeOrName, attributes, innerBlocks = [] ) /** * Filters the props applied to the block save result element. * - * @param {Object} props Props applied to save element. - * @param {WPBlockType} blockType Block type definition. - * @param {Object} attributes Block attributes. + * @param {Object} props Props applied to save element. + * @param {WPBlock} blockType Block type definition. + * @param {Object} attributes Block attributes. */ const props = applyFilters( 'blocks.getSaveContent.extraProps', @@ -104,9 +104,9 @@ export function getSaveElement( blockTypeOrName, attributes, innerBlocks = [] ) /** * Filters the save result of a block during serialization. * - * @param {WPElement} element Block save result. - * @param {WPBlockType} blockType Block type definition. - * @param {Object} attributes Block attributes. + * @param {WPElement} element Block save result. + * @param {WPBlock} blockType Block type definition. + * @param {Object} attributes Block attributes. */ element = applyFilters( 'blocks.getSaveElement', element, blockType, attributes ); @@ -150,28 +150,27 @@ export function getSaveContent( blockTypeOrName, attributes, innerBlocks ) { * @return {Object<string,*>} Subset of attributes for comment serialization. */ export function getCommentAttributes( blockType, attributes ) { - return reduce( blockType.attributes, ( result, attributeSchema, key ) => { + return reduce( blockType.attributes, ( accumulator, attributeSchema, key ) => { const value = attributes[ key ]; - // Ignore undefined values. if ( undefined === value ) { - return result; + return accumulator; } // Ignore all attributes but the ones with an "undefined" source // "undefined" source refers to attributes saved in the block comment. if ( attributeSchema.source !== undefined ) { - return result; + return accumulator; } // Ignore default value. if ( 'default' in attributeSchema && attributeSchema.default === value ) { - return result; + return accumulator; } // Otherwise, include in comment set. - result[ key ] = value; - return result; + accumulator[ key ] = value; + return accumulator; }, {} ); } diff --git a/packages/blocks/src/block-content-provider/index.js b/packages/blocks/src/block-content-provider/index.js index 352550a87be80f..96b9a4d31bcd1b 100644 --- a/packages/blocks/src/block-content-provider/index.js +++ b/packages/blocks/src/block-content-provider/index.js @@ -26,7 +26,7 @@ const { Consumer, Provider } = createContext( () => {} ); * </BlockContentProvider> * ``` * - * @return {WPElement} Element with BlockContent injected via context. + * @return {WPComponent} Element with BlockContent injected via context. */ const BlockContentProvider = ( { children, innerBlocks } ) => { const BlockContent = () => { @@ -48,7 +48,7 @@ const BlockContentProvider = ( { children, innerBlocks } ) => { * A Higher Order Component used to inject BlockContent using context to the * wrapped component. * - * @return {Component} Enhanced component with injected BlockContent as prop. + * @return {WPComponent} Enhanced component with injected BlockContent as prop. */ export const withBlockContentContext = createHigherOrderComponent( ( OriginalComponent ) => { return ( props ) => ( diff --git a/packages/components/package.json b/packages/components/package.json index d19667eca46cf5..c542fc3a79a7e0 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "8.3.0", + "version": "8.3.2", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/src/autocomplete/index.js b/packages/components/src/autocomplete/index.js index 283ccd92633b04..b12f97b30367ba 100644 --- a/packages/components/src/autocomplete/index.js +++ b/packages/components/src/autocomplete/index.js @@ -91,7 +91,7 @@ import withSpokenMessages from '../higher-order/with-spoken-messages'; */ /** - * @typedef {Object} Completer + * @typedef {Object} WPCompleter * @property {string} name a way to identify a completer, useful for selective overriding. * @property {?string} className A class to apply to the popup menu. * @property {string} triggerPrefix the prefix that will display the menu. @@ -232,8 +232,8 @@ export class Autocomplete extends Component { /** * Load options for an autocompleter. * - * @param {Completer} completer The autocompleter. - * @param {string} query The query, if any. + * @param {WPCompleter} completer The autocompleter. + * @param {string} query The query, if any. */ loadOptions( completer, query ) { const { options } = completer; diff --git a/packages/components/src/button-group/stories/index.js b/packages/components/src/button-group/stories/index.js index 1e1646e0268d15..8b1a694691025c 100644 --- a/packages/components/src/button-group/stories/index.js +++ b/packages/components/src/button-group/stories/index.js @@ -4,7 +4,7 @@ import Button from '../../button'; import ButtonGroup from '../'; -export default { title: 'Button Group', component: ButtonGroup }; +export default { title: 'ButtonGroup', component: ButtonGroup }; export const _default = () => { const style = { margin: '0 4px' }; diff --git a/packages/components/src/button/stories/index.js b/packages/components/src/button/stories/index.js index ae949a1cd72906..b3c3b408c373ec 100644 --- a/packages/components/src/button/stories/index.js +++ b/packages/components/src/button/stories/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { text } from '@storybook/addon-knobs'; + /** * Internal dependencies */ @@ -5,32 +10,78 @@ import Button from '../'; export default { title: 'Button', component: Button }; -export const _default = () => <Button>Hello Button</Button>; +export const _default = () => { + const label = text( 'Label', 'Default Button' ); + + return ( + <Button>{ label }</Button> + ); +}; + +export const primary = () => { + const label = text( 'Label', 'Primary Button' ); + + return ( + <Button isPrimary>{ label }</Button> + ); +}; + +export const large = () => { + const label = text( 'Label', 'Large Button' ); + + return ( + <Button isLarge>{ label }</Button> + ); +}; + +export const largePrimary = () => { + const label = text( 'Label', 'Large Primary Button' ); + + return ( + <Button isPrimary isLarge>{ label }</Button> + ); +}; + +export const small = () => { + const label = text( 'Label', 'Small Button' ); + + return ( + <Button isSmall>{ label }</Button> + ); +}; -export const primary = () => <Button isPrimary>Hello Button</Button>; +export const toggled = () => { + const label = text( 'Label', 'Toggled Button' ); -export const large = () => <Button isLarge>Hello Button</Button>; + return ( + <Button isToggled>{ label }</Button> + ); +}; -export const largePrimary = () => ( - <Button isPrimary isLarge> - Hello Button - </Button> -); +export const disabled = () => { + const label = text( 'Label', 'Disabled Button' ); -export const small = () => <Button isSmall>Hello Button</Button>; + return ( + <Button disabled>{ label }</Button> + ); +}; -export const toggled = () => <Button isToggled>Hello Button</Button>; +export const link = () => { + const label = text( 'Label', 'Link Button' ); -export const disabled = () => <Button disabled>Hello Button</Button>; + return ( + <Button href="https://wordpress.org/" target="_blank"> + { label } + </Button> + ); +}; -export const link = () => ( - <Button href="https://wordpress.org/" target="_blank"> - Hello Button - </Button> -); +export const disabledLink = () => { + const label = text( 'Label', 'Disabled Link Button' ); -export const disabledLink = () => ( - <Button href="https://wordpress.org/" target="_blank" disabled> - Hello Button - </Button> -); + return ( + <Button href="https://wordpress.org/" target="_blank" disabled> + { label } + </Button> + ); +}; diff --git a/packages/components/src/button/style.scss b/packages/components/src/button/style.scss index 6d3f94a18a1c24..2f62f199794eda 100644 --- a/packages/components/src/button/style.scss +++ b/packages/components/src/button/style.scss @@ -37,7 +37,7 @@ background: #f3f5f6; color: color(theme(button) shade(25%)); border-color: color(theme(button) shade(5%)); - box-shadow: 0 0 0 1px color(theme(button) shade(5%)); + box-shadow: 0 0 0 $border-width color(theme(button) shade(5%)); text-decoration: none; } @@ -54,7 +54,7 @@ color: #a0a5aa; border-color: #ddd; background: #f7f7f7; - text-shadow: 0 1px 0 #fff; + text-shadow: 0 $border-width 0 #fff; transform: none; opacity: 1; } @@ -76,7 +76,7 @@ &:focus:enabled { box-shadow: - 0 0 0 1px $white, + 0 0 0 $border-width $white, 0 0 0 3px color(theme(button)); } @@ -107,7 +107,7 @@ &:focus:enabled { box-shadow: - 0 0 0 1px $white, + 0 0 0 $border-width $white, 0 0 0 3px color(theme(button)); } } @@ -157,8 +157,8 @@ &:focus { color: #124964; box-shadow: - 0 0 0 1px #5b9dd9, - 0 0 2px 1px rgba(30, 140, 190, 0.8); + 0 0 0 $border-width #5b9dd9, + 0 0 2px $border-width rgba(30, 140, 190, 0.8); } } diff --git a/packages/components/src/checkbox-control/README.md b/packages/components/src/checkbox-control/README.md index 43165e8ae1b59c..b6245fbd036cd0 100644 --- a/packages/components/src/checkbox-control/README.md +++ b/packages/components/src/checkbox-control/README.md @@ -56,17 +56,16 @@ If only a few child checkboxes are checked, the parent checkbox becomes a mixed Render an is author checkbox: ```jsx import { CheckboxControl } from '@wordpress/components'; -import { withState } from '@wordpress/compose'; +import { useState } from '@wordpress/element'; -const MyCheckboxControl = withState( { - isChecked: true, -} )( ( { isChecked, setState } ) => ( +const MyCheckboxControl = () => ( + const [ isChecked, setChecked ] = useState( true ); <CheckboxControl heading="User" label="Is author" help="Is the user a author or not?" checked={ isChecked } - onChange={ ( isChecked ) => { setState( { isChecked } ) } } + onChange={ setChecked } /> ) ); ``` diff --git a/packages/components/src/checkbox-control/stories/index.js b/packages/components/src/checkbox-control/stories/index.js new file mode 100644 index 00000000000000..40b48ccf3ef3a2 --- /dev/null +++ b/packages/components/src/checkbox-control/stories/index.js @@ -0,0 +1,54 @@ +/** + * External dependencies + */ +import { text } from '@storybook/addon-knobs'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import CheckboxControl from '../'; + +export default { title: 'CheckboxControl', component: CheckboxControl }; + +const CheckboxControlWithState = ( { checked, ...props } ) => { + const [ isChecked, setChecked ] = useState( checked ); + + return ( + <CheckboxControl + { ...props } + checked={ isChecked } + onChange={ setChecked } + /> + ); +}; + +export const _default = () => { + const label = text( 'Label', 'Is author' ); + + return ( + <CheckboxControlWithState + label={ label } + checked + /> + ); +}; + +export const all = () => { + const heading = text( 'Heading', 'User' ); + const label = text( 'Label', 'Is author' ); + const help = text( 'Help', 'Is the user an author or not?' ); + + return ( + <CheckboxControlWithState + heading={ heading } + label={ label } + help={ help } + checked + /> + ); +}; diff --git a/packages/components/src/clipboard-button/stories/index.js b/packages/components/src/clipboard-button/stories/index.js index 25632614eb249d..7e0146448d56d5 100644 --- a/packages/components/src/clipboard-button/stories/index.js +++ b/packages/components/src/clipboard-button/stories/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { boolean, text } from '@storybook/addon-knobs'; + /** * WordPress dependencies */ @@ -8,18 +13,30 @@ import { useState } from '@wordpress/element'; */ import ClipboardButton from '../'; -export default { title: 'Clipboard Button', component: ClipboardButton }; +export default { title: 'ClipboardButton', component: ClipboardButton }; + +const ClipboardButtonWithState = ( { copied, ...props } ) => { + const [ isCopied, setCopied ] = useState( copied ); -export const _default = () => { - const [ copied, setCopied ] = useState( false ); return ( <ClipboardButton - isPrimary - text="Text" + { ...props } onCopy={ () => setCopied( true ) } onFinishCopy={ () => setCopied( false ) } > - { copied ? 'Copied!' : 'Copy "Text"' } + { isCopied ? 'Copied!' : `Copy "${ props.text }"` } </ClipboardButton> ); }; + +export const _default = () => { + const isPrimary = boolean( 'Is primary', true ); + const copyText = text( 'Text', 'Text' ); + + return ( + <ClipboardButtonWithState + isPrimary={ isPrimary } + text={ copyText } + /> + ); +}; diff --git a/packages/components/src/color-indicator/stories/index.js b/packages/components/src/color-indicator/stories/index.js index 3e9400b0919c11..d14f180abbe25a 100644 --- a/packages/components/src/color-indicator/stories/index.js +++ b/packages/components/src/color-indicator/stories/index.js @@ -1,13 +1,21 @@ +/** + * External dependencies + */ +import { text } from '@storybook/addon-knobs'; + /** * Internal dependencies */ import ColorIndicator from '../'; export default { - title: 'Color Indicator', + title: 'ColorIndicator', component: ColorIndicator, }; -export const _default = () => ( - <ColorIndicator colorValue="#0073aa" /> -); +export const _default = () => { + const color = text( 'Color', '#0073aa' ); + return ( + <ColorIndicator colorValue={ color } /> + ); +}; diff --git a/packages/components/src/color-palette/stories/index.js b/packages/components/src/color-palette/stories/index.js new file mode 100644 index 00000000000000..3cc1f4f8ff66df --- /dev/null +++ b/packages/components/src/color-palette/stories/index.js @@ -0,0 +1,56 @@ +/** + * External dependencies + */ +import { object } from '@storybook/addon-knobs'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import ColorPalette from '../'; + +export default { title: 'ColorPalette', component: ColorPalette }; + +const ColorPaletteWithState = ( props ) => { + const [ color, setColor ] = useState( '#F00' ); + return ( + <ColorPalette + { ...props } + value={ color } + onChange={ setColor } + /> + ); +}; + +export const _default = () => { + const colors = [ + { name: 'red', color: '#f00' }, + { name: 'white', color: '#fff' }, + { name: 'blue', color: '#00f' }, + ]; + + return ( + <ColorPaletteWithState + colors={ colors } + /> + ); +}; + +export const withKnobs = () => { + const colors = [ + object( 'Red', { name: 'red', color: '#f00' } ), + object( 'White', { name: 'white', color: '#fff' } ), + object( 'Blue', { name: 'blue', color: '#00f' } ), + ]; + + return ( + <ColorPaletteWithState + colors={ colors } + /> + ); +}; + diff --git a/packages/components/src/color-picker/hue.js b/packages/components/src/color-picker/hue.js index 214df7e725bdc1..0c97020d89efdc 100644 --- a/packages/components/src/color-picker/hue.js +++ b/packages/components/src/color-picker/hue.js @@ -43,6 +43,7 @@ import { TAB } from '@wordpress/keycodes'; */ import { calculateHueChange } from './utils'; import KeyboardShortcuts from '../keyboard-shortcuts'; +import VisuallyHidden from '../visually-hidden'; export class Hue extends Component { constructor() { @@ -157,12 +158,11 @@ export class Hue extends Component { style={ pointerLocation } onKeyDown={ this.preventKeyEvents } /> - <p - className="components-color-picker__hue-description screen-reader-text" + <VisuallyHidden as="p" id={ `components-color-picker__hue-description-${ instanceId }` } > { __( 'Move the arrow left or right to change hue.' ) } - </p> + </VisuallyHidden> </div> { /* eslint-enable jsx-a11y/no-static-element-interactions */ } </div> diff --git a/packages/components/src/color-picker/inputs.js b/packages/components/src/color-picker/inputs.js index 39817d13edb74f..42e77a31e71820 100644 --- a/packages/components/src/color-picker/inputs.js +++ b/packages/components/src/color-picker/inputs.js @@ -17,6 +17,7 @@ import { pure } from '@wordpress/compose'; */ import IconButton from '../icon-button'; import TextControl from '../text-control'; +import VisuallyHidden from '../visually-hidden'; import { isValidHex } from './utils'; /* Wrapper for TextControl, only used to handle intermediate state while typing. */ @@ -175,9 +176,9 @@ export class Inputs extends Component { } else if ( this.state.view === 'rgb' ) { return ( <fieldset> - <legend className="screen-reader-text"> + <VisuallyHidden as="legend"> { __( 'Color value in RGB' ) } - </legend> + </VisuallyHidden> <div className="components-color-picker__inputs-fields"> <Input source={ this.state.view } @@ -228,9 +229,9 @@ export class Inputs extends Component { } else if ( this.state.view === 'hsl' ) { return ( <fieldset> - <legend className="screen-reader-text"> + <VisuallyHidden as="legend"> { __( 'Color value in HSL' ) } - </legend> + </VisuallyHidden> <div className="components-color-picker__inputs-fields"> <Input source={ this.state.view } diff --git a/packages/components/src/color-picker/saturation.js b/packages/components/src/color-picker/saturation.js index 3bdd10bfcdfe28..ae1f80333237d5 100644 --- a/packages/components/src/color-picker/saturation.js +++ b/packages/components/src/color-picker/saturation.js @@ -43,6 +43,7 @@ import { compose, pure, withInstanceId } from '@wordpress/compose'; */ import { calculateSaturationChange } from './utils'; import KeyboardShortcuts from '../keyboard-shortcuts'; +import VisuallyHidden from '../visually-hidden'; export class Saturation extends Component { constructor( props ) { @@ -171,13 +172,12 @@ export class Saturation extends Component { style={ pointerLocation } onKeyDown={ this.preventKeyEvents } /> - <div - className="screen-reader-text" + <VisuallyHidden id={ `color-picker-saturation-${ instanceId }` }> { __( 'Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to decrease saturation, and right to increase saturation.' ) } - </div> + </VisuallyHidden> </div> </KeyboardShortcuts> ); diff --git a/packages/components/src/color-picker/stories/index.js b/packages/components/src/color-picker/stories/index.js new file mode 100644 index 00000000000000..19bca39729d4f5 --- /dev/null +++ b/packages/components/src/color-picker/stories/index.js @@ -0,0 +1,39 @@ + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import ColorPicker from '../'; + +export default { title: 'ColorPicker', component: ColorPicker }; + +const ColorPickerWithState = ( { ...props } ) => { + const [ color, setColor ] = useState( '#f00' ); + return ( + <ColorPicker + { ...props } + color={ color } + onChangeComplete={ ( value ) => setColor( value.hex ) } + /> + ); +}; + +export const _default = () => { + return ( + <ColorPickerWithState + disableAlpha + /> + ); +}; + +export const alphaEnabled = () => { + return ( + <ColorPickerWithState + disableAlpha={ false } + /> + ); +}; diff --git a/packages/components/src/color-picker/test/__snapshots__/index.js.snap b/packages/components/src/color-picker/test/__snapshots__/index.js.snap index 5d61a24e5ac9e6..a3713f860f3c01 100644 --- a/packages/components/src/color-picker/test/__snapshots__/index.js.snap +++ b/packages/components/src/color-picker/test/__snapshots__/index.js.snap @@ -39,7 +39,7 @@ exports[`ColorPicker should commit changes to all views on blur 1`] = ` } /> <div - className="screen-reader-text" + className="components-visually-hidden" id="color-picker-saturation-2" > Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to decrease saturation, and right to increase saturation. @@ -99,7 +99,7 @@ exports[`ColorPicker should commit changes to all views on blur 1`] = ` tabIndex="0" /> <p - className="components-color-picker__hue-description screen-reader-text" + className="components-visually-hidden" id="components-color-picker__hue-description-2" > Move the arrow left or right to change hue. @@ -213,7 +213,7 @@ exports[`ColorPicker should commit changes to all views on keyDown = DOWN 1`] = } /> <div - className="screen-reader-text" + className="components-visually-hidden" id="color-picker-saturation-4" > Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to decrease saturation, and right to increase saturation. @@ -273,7 +273,7 @@ exports[`ColorPicker should commit changes to all views on keyDown = DOWN 1`] = tabIndex="0" /> <p - className="components-color-picker__hue-description screen-reader-text" + className="components-visually-hidden" id="components-color-picker__hue-description-4" > Move the arrow left or right to change hue. @@ -387,7 +387,7 @@ exports[`ColorPicker should commit changes to all views on keyDown = ENTER 1`] = } /> <div - className="screen-reader-text" + className="components-visually-hidden" id="color-picker-saturation-5" > Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to decrease saturation, and right to increase saturation. @@ -447,7 +447,7 @@ exports[`ColorPicker should commit changes to all views on keyDown = ENTER 1`] = tabIndex="0" /> <p - className="components-color-picker__hue-description screen-reader-text" + className="components-visually-hidden" id="components-color-picker__hue-description-5" > Move the arrow left or right to change hue. @@ -561,7 +561,7 @@ exports[`ColorPicker should commit changes to all views on keyDown = UP 1`] = ` } /> <div - className="screen-reader-text" + className="components-visually-hidden" id="color-picker-saturation-3" > Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to decrease saturation, and right to increase saturation. @@ -621,7 +621,7 @@ exports[`ColorPicker should commit changes to all views on keyDown = UP 1`] = ` tabIndex="0" /> <p - className="components-color-picker__hue-description screen-reader-text" + className="components-visually-hidden" id="components-color-picker__hue-description-3" > Move the arrow left or right to change hue. @@ -735,7 +735,7 @@ exports[`ColorPicker should only update input view for draft changes 1`] = ` } /> <div - className="screen-reader-text" + className="components-visually-hidden" id="color-picker-saturation-1" > Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to decrease saturation, and right to increase saturation. @@ -795,7 +795,7 @@ exports[`ColorPicker should only update input view for draft changes 1`] = ` tabIndex="0" /> <p - className="components-color-picker__hue-description screen-reader-text" + className="components-visually-hidden" id="components-color-picker__hue-description-1" > Move the arrow left or right to change hue. @@ -909,7 +909,7 @@ exports[`ColorPicker should render color picker 1`] = ` } /> <div - className="screen-reader-text" + className="components-visually-hidden" id="color-picker-saturation-0" > Use your arrow keys to change the base color. Move up to lighten the color, down to darken, left to decrease saturation, and right to increase saturation. @@ -969,7 +969,7 @@ exports[`ColorPicker should render color picker 1`] = ` tabIndex="0" /> <p - className="components-color-picker__hue-description screen-reader-text" + className="components-visually-hidden" id="components-color-picker__hue-description-0" > Move the arrow left or right to change hue. diff --git a/packages/components/src/dashicon/stories/index.js b/packages/components/src/dashicon/stories/index.js new file mode 100644 index 00000000000000..5ba7d7be98cbcb --- /dev/null +++ b/packages/components/src/dashicon/stories/index.js @@ -0,0 +1,25 @@ +/** + * External dependencies + */ +import { number, text } from '@storybook/addon-knobs'; + +/** + * Internal dependencies + */ +import Dashicon from '../'; + +export default { title: 'Dashicon', component: Dashicon }; + +export const _default = () => { + const icon = text( 'Icon', 'wordpress' ); + const color = text( 'Color', '#0079AA' ); + const size = number( 'Size', 20 ); + + return ( + <Dashicon + icon={ icon } + color={ color } + size={ size } + /> + ); +}; diff --git a/packages/components/src/date-time/date.js b/packages/components/src/date-time/date.js index 7361494a61e688..7b1accff50227d 100644 --- a/packages/components/src/date-time/date.js +++ b/packages/components/src/date-time/date.js @@ -65,7 +65,7 @@ class DatePicker extends Component { * object representing now. If a null value is passed, return a null value. * * @param {?string} currentDate Date representing the currently selected date or null to signify no selection. - * @return {?Moment} Moment object for selected date or null. + * @return {?moment.Moment} Moment object for selected date or null. */ getMomentDate( currentDate ) { if ( null === currentDate ) { diff --git a/packages/components/src/date-time/index.js b/packages/components/src/date-time/index.js index 97bd891cac9a7c..bb7434d2e82ce3 100644 --- a/packages/components/src/date-time/index.js +++ b/packages/components/src/date-time/index.js @@ -34,7 +34,7 @@ export class DateTimePicker extends Component { } render() { - const { currentDate, is12Hour, onChange } = this.props; + const { currentDate, is12Hour, isInvalidDate, onChange } = this.props; return ( <div className="components-datetime"> @@ -48,6 +48,7 @@ export class DateTimePicker extends Component { <DatePicker currentDate={ currentDate } onChange={ onChange } + isInvalidDate={ isInvalidDate } /> </> ) } diff --git a/packages/components/src/dimension-control/README.md b/packages/components/src/dimension-control/README.md new file mode 100644 index 00000000000000..435fdf4e73b046 --- /dev/null +++ b/packages/components/src/dimension-control/README.md @@ -0,0 +1,120 @@ +DimensionControl +============================= + +`DimensionControl` is a component designed to provide a UI to control spacing and/or dimensions. + +## Usage + +In a block's `edit` implementation, render a `<DimensionControl />` component. + + +```jsx +import { registerBlockType } from '@wordpress/blocks'; +import { __ } from '@wordpress/i18n'; +import { + DimensionControl, +} from '@wordpress/block-editor'; + +registerBlockType( 'my-plugin/my-block', { + // ... + + attributes: { + // other attributes here + // ... + + paddingSize: { + type: 'string', + }, + }, + + edit( { attributes, setAttributes, clientId } ) { + + const { paddingSize } = attributes; + + + const updateSpacing = ( dimension, size, device = '' ) => { + setAttributes( { + [ `${ dimension }${ device }` ]: size, + } ); + }; + + return ( + <DimensionControl + label={ __( 'Padding' ) } + icon={ 'desktop' } + onChange={ partialRight( updateSpacing, 'paddingSize' ) } + value={ paddingSize } + /> + ); + } +} ); +``` + +_Note:_ it is recommended to partially apply the value of the Block attribute to be updated (eg: `paddingSize`, `marginSize`...etc) to your callback functions. This avoids the need to unnecessarily couple the component to the Block attribute schema. + +_Note:_ by default, if you do not provide an initial `value` prop for the current dimension value, then no value will be selected (ie: there is no default dimension set). + +## Props + +### `label` +* **Type:** `String` +* **Default:** `undefined` +* **Required:** Yes + +The human readable label for the control. + +### `value` +* **Type:** `String` +* **Default:** `''` +* **Required:** No + +The current value of the dimension UI control. If provided the UI with automatically select the value. + +### `sizes` +* **Type:** `Array` +* **Default:** See `packages/block-editor/src/components/dimension-control/sizes.js` +* **Required:** No + +An optional array of size objects in the following shape: + +``` +[ + { + name: __( 'Small' ), + slug: 'small', + }, + { + name: __( 'Medium' ), + slug: 'small', + }, + // ...etc +] +``` + +By default a set of relative sizes (`small`, `medium`...etc) are provided. See `packages/block-editor/src/components/dimension-control/sizes.js`. + +### `icon` +* **Type:** `String` +* **Default:** `undefined` +* **Required:** No + +An optional dashicon to display before to the control label. + +### `onChange` +* **Type:** `Function` +* **Default:** `undefined` +* **Required:** No +* **Arguments:**: + - `size` - a string representing the selected size (eg: `medium`) + +A callback which is triggered when a spacing size value changes (is selected/clicked). + + +### `className` +* **Type:** `String` +* **Default:** `''` +* **Required:** No + +A string of classes to be added to the control component. + + diff --git a/packages/components/src/dimension-control/index.js b/packages/components/src/dimension-control/index.js new file mode 100644 index 00000000000000..c69b80f5c49e02 --- /dev/null +++ b/packages/components/src/dimension-control/index.js @@ -0,0 +1,76 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { isFunction } from 'lodash'; + +/** + * WordPress dependencies + */ +/** + * Internal dependencies + */ +import { + Icon, + SelectControl, +} from '../'; +import { __ } from '@wordpress/i18n'; + +import { + Fragment, +} from '@wordpress/element'; + +/** + * Internal dependencies + */ +import sizesTable, { findSizeBySlug } from './sizes'; + +export function DimensionControl( props ) { + const { label, value, sizes = sizesTable, icon, onChange, className = '' } = props; + + const onChangeSpacingSize = ( val ) => { + const theSize = findSizeBySlug( sizes, val ); + + if ( ! theSize || value === theSize.slug ) { + onChange( undefined ); + } else if ( isFunction( onChange ) ) { + onChange( theSize.slug ); + } + }; + + const formatSizesAsOptions = ( theSizes ) => { + const options = theSizes.map( ( { name, slug } ) => ( { + label: name, + value: slug, + } ) ); + + return [ { + label: __( 'Default' ), + value: '', + } ].concat( options ); + }; + + const selectLabel = ( + <Fragment> + { icon && ( + <Icon + icon={ icon } + /> + ) } + { label } + </Fragment> + ); + + return ( + <SelectControl + className={ classnames( className, 'block-editor-dimension-control' ) } + label={ selectLabel } + hideLabelFromVision={ false } + value={ value } + onChange={ onChangeSpacingSize } + options={ formatSizesAsOptions( sizes ) } + /> + ); +} + +export default DimensionControl; diff --git a/packages/components/src/dimension-control/sizes.js b/packages/components/src/dimension-control/sizes.js new file mode 100644 index 00000000000000..41fa0717028725 --- /dev/null +++ b/packages/components/src/dimension-control/sizes.js @@ -0,0 +1,45 @@ +/** + * Sizes + * + * defines the sizes used in dimension controls + * all hardcoded `size` values are based on the value of + * the Sass variable `$block-padding` from + * `packages/block-editor/src/components/dimension-control/sizes.js`. + */ + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Finds the correct size object from the provided sizes + * table by size slug (eg: `medium`) + * + * @param {Array} sizes containing objects for each size definition + * @param {string} slug a string representation of the size (eg: `medium`) + * @return {Object} the matching size definition + */ +export const findSizeBySlug = ( sizes, slug ) => sizes.find( ( size ) => slug === size.slug ); + +export default [ + { + name: __( 'None' ), + slug: 'none', + }, + { + name: __( 'Small' ), + slug: 'small', + }, + { + name: __( 'Medium' ), + slug: 'medium', + }, + { + name: __( 'Large' ), + slug: 'large', + }, { + name: __( 'Extra Large' ), + slug: 'xlarge', + }, +]; diff --git a/packages/components/src/dimension-control/style.scss b/packages/components/src/dimension-control/style.scss new file mode 100644 index 00000000000000..7f1481747dfe1a --- /dev/null +++ b/packages/components/src/dimension-control/style.scss @@ -0,0 +1,22 @@ +.block-editor-dimension-control { + + .components-base-control__field { + display: flex; + align-items: center; + } + + .components-base-control__label { + display: flex; + align-items: center; + margin-right: 1em; + margin-bottom: 0; + + .dashicon { + margin-right: 0.5em; + } + } + + &.is-manual .components-base-control__label { + width: 10em; + } +} diff --git a/packages/components/src/dimension-control/test/__snapshots__/index.test.js.snap b/packages/components/src/dimension-control/test/__snapshots__/index.test.js.snap new file mode 100644 index 00000000000000..c06662ee862c99 --- /dev/null +++ b/packages/components/src/dimension-control/test/__snapshots__/index.test.js.snap @@ -0,0 +1,163 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DimensionControl rendering renders with custom sizes 1`] = ` +<WithInstanceId(SelectControl) + className="block-editor-dimension-control" + hideLabelFromVision={false} + label={ + <React.Fragment> + Custom Dimension + </React.Fragment> + } + onChange={[Function]} + options={ + Array [ + Object { + "label": "Default", + "value": "", + }, + Object { + "label": "Mini", + "value": "mini", + }, + Object { + "label": "Middle", + "value": "middle", + }, + Object { + "label": "Giant", + "value": "giant", + }, + ] + } +/> +`; + +exports[`DimensionControl rendering renders with defaults 1`] = ` +<WithInstanceId(SelectControl) + className="block-editor-dimension-control" + hideLabelFromVision={false} + label={ + <React.Fragment> + Padding + </React.Fragment> + } + onChange={[Function]} + options={ + Array [ + Object { + "label": "Default", + "value": "", + }, + Object { + "label": "None", + "value": "none", + }, + Object { + "label": "Small", + "value": "small", + }, + Object { + "label": "Medium", + "value": "medium", + }, + Object { + "label": "Large", + "value": "large", + }, + Object { + "label": "Extra Large", + "value": "xlarge", + }, + ] + } +/> +`; + +exports[`DimensionControl rendering renders with icon and custom icon label 1`] = ` +<WithInstanceId(SelectControl) + className="block-editor-dimension-control" + hideLabelFromVision={false} + label={ + <React.Fragment> + <Icon + icon="tablet" + /> + Margin + </React.Fragment> + } + onChange={[Function]} + options={ + Array [ + Object { + "label": "Default", + "value": "", + }, + Object { + "label": "None", + "value": "none", + }, + Object { + "label": "Small", + "value": "small", + }, + Object { + "label": "Medium", + "value": "medium", + }, + Object { + "label": "Large", + "value": "large", + }, + Object { + "label": "Extra Large", + "value": "xlarge", + }, + ] + } +/> +`; + +exports[`DimensionControl rendering renders with icon and default icon label 1`] = ` +<WithInstanceId(SelectControl) + className="block-editor-dimension-control" + hideLabelFromVision={false} + label={ + <React.Fragment> + <Icon + icon="tablet" + /> + Margin + </React.Fragment> + } + onChange={[Function]} + options={ + Array [ + Object { + "label": "Default", + "value": "", + }, + Object { + "label": "None", + "value": "none", + }, + Object { + "label": "Small", + "value": "small", + }, + Object { + "label": "Medium", + "value": "medium", + }, + Object { + "label": "Large", + "value": "large", + }, + Object { + "label": "Extra Large", + "value": "xlarge", + }, + ] + } +/> +`; diff --git a/packages/components/src/dimension-control/test/index.test.js b/packages/components/src/dimension-control/test/index.test.js new file mode 100644 index 00000000000000..0b053a493ee123 --- /dev/null +++ b/packages/components/src/dimension-control/test/index.test.js @@ -0,0 +1,128 @@ +/** + * External dependencies + */ +import { shallow, mount } from 'enzyme'; +import { uniqueId } from 'lodash'; + +/** + * Internal dependencies + */ +import { DimensionControl } from '../'; + +describe( 'DimensionControl', () => { + const onChangeHandler = jest.fn(); + + afterEach( () => { + onChangeHandler.mockClear(); + } ); + + describe( 'rendering', () => { + it( 'renders with defaults', () => { + const wrapper = shallow( + <DimensionControl + instanceId={ uniqueId() } + label={ 'Padding' } + /> + ); + expect( wrapper ).toMatchSnapshot(); + } ); + + it( 'renders with icon and default icon label', () => { + const wrapper = shallow( + <DimensionControl + instanceId={ uniqueId() } + label={ 'Margin' } + icon={ 'tablet' } + /> + ); + expect( wrapper ).toMatchSnapshot(); + } ); + + it( 'renders with icon and custom icon label', () => { + const wrapper = shallow( + <DimensionControl + instanceId={ uniqueId() } + label={ 'Margin' } + icon={ 'tablet' } + iconLabel={ 'Tablet Devices' } + /> + ); + expect( wrapper ).toMatchSnapshot(); + } ); + + it( 'renders with custom sizes', () => { + const customSizes = [ + { + name: 'Mini', + size: 1, + slug: 'mini', + }, + { + name: 'Middle', + size: 5, + slug: 'middle', + }, + { + name: 'Giant', + size: 10, + slug: 'giant', + }, + ]; + + const wrapper = shallow( + <DimensionControl + instanceId={ uniqueId() } + label={ 'Custom Dimension' } + sizes={ customSizes } + /> + ); + expect( wrapper ).toMatchSnapshot(); + } ); + } ); + + describe( 'callbacks', () => { + it( 'should call onChange handler with correct args on size change', () => { + const wrapper = mount( + <DimensionControl + instanceId={ uniqueId() } + label={ 'Padding' } + onChange={ onChangeHandler } + /> + ); + + wrapper.find( 'select' ).at( 0 ).simulate( 'change', { + target: { + value: 'small', + }, + } ); + + wrapper.find( 'select' ).at( 0 ).simulate( 'change', { + target: { + value: 'medium', + }, + } ); + + expect( onChangeHandler ).toHaveBeenCalledTimes( 2 ); + expect( onChangeHandler.mock.calls[ 0 ][ 0 ] ).toEqual( 'small' ); + expect( onChangeHandler.mock.calls[ 1 ][ 0 ] ).toEqual( 'medium' ); + } ); + + it( 'should call onChange handler with undefined value when no size is provided on change', () => { + const wrapper = mount( + <DimensionControl + instanceId={ uniqueId() } + label={ 'Padding' } + onChange={ onChangeHandler } + /> + ); + + wrapper.find( 'select' ).at( 0 ).simulate( 'change', { + target: { + value: '', // this happens when you select the "default" <option /> + }, + } ); + + expect( onChangeHandler ).toHaveBeenNthCalledWith( 1, undefined ); + } ); + } ); +} ); diff --git a/packages/components/src/draggable/stories/index.js b/packages/components/src/draggable/stories/index.js new file mode 100644 index 00000000000000..fbe81321df7232 --- /dev/null +++ b/packages/components/src/draggable/stories/index.js @@ -0,0 +1,69 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Draggable from '../'; +import Dashicon from '../../dashicon'; + +export default { title: 'Draggable', component: Draggable }; + +const Box = ( props ) => { + return ( + <div + { ...props } + style={ { + alignItems: 'center', + display: 'flex', + justifyContent: 'center', + width: 100, + height: 100, + background: '#ddd', + } } + /> + ); +}; + +const DraggalbeExample = () => { + const [ isDragging, setDragging ] = useState( false ); + + // Allow for the use of ID in the example + /* eslint-disable no-restricted-syntax */ + return ( + <div> + <p>Is Dragging? { isDragging ? 'Yes' : 'No' }</p> + <div id="draggable-example-box" style={ { display: 'inline-flex' } }> + <Draggable elementId="draggable-example-box"> + { ( { onDraggableStart, onDraggableEnd } ) => { + const handleOnDragStart = ( event ) => { + setDragging( true ); + onDraggableStart( event ); + }; + const handleOnDragEnd = ( event ) => { + setDragging( false ); + onDraggableEnd( event ); + }; + + return ( + <Box + onDragStart={ handleOnDragStart } + onDragEnd={ handleOnDragEnd } + draggable + > + <Dashicon icon="move" /> + </Box> + ); + } } + </Draggable> + </div> + </div> + ); + /* eslint-enable no-restricted-syntax */ +}; + +export const _default = () => { + return <DraggalbeExample />; +}; diff --git a/packages/components/src/external-link/index.js b/packages/components/src/external-link/index.js index 68bf56394b8f86..cc9750a4b779c9 100644 --- a/packages/components/src/external-link/index.js +++ b/packages/components/src/external-link/index.js @@ -14,6 +14,7 @@ import { forwardRef } from '@wordpress/element'; * Internal dependencies */ import Dashicon from '../dashicon'; +import VisuallyHidden from '../visually-hidden'; export function ExternalLink( { href, children, className, rel = '', ...additionalProps }, ref ) { rel = uniq( compact( [ @@ -27,12 +28,12 @@ export function ExternalLink( { href, children, className, rel = '', ...addition // eslint-disable-next-line react/jsx-no-target-blank <a { ...additionalProps } className={ classes } href={ href } target="_blank" rel={ rel } ref={ ref }> { children } - <span className="screen-reader-text"> + <VisuallyHidden as="span"> { /* translators: accessibility text */ __( '(opens in a new tab)' ) } - </span> + </VisuallyHidden> <Dashicon icon="external" className="components-external-link__icon" /> </a> ); diff --git a/packages/components/src/external-link/stories/index.js b/packages/components/src/external-link/stories/index.js new file mode 100644 index 00000000000000..9abfa1af04ba9c --- /dev/null +++ b/packages/components/src/external-link/stories/index.js @@ -0,0 +1,17 @@ +/** + * External dependencies + */ +import { text } from '@storybook/addon-knobs'; +/** + * Internal dependencies + */ +import ExternalLink from '../'; + +export default { title: 'ExternalLink', component: ExternalLink }; + +export const _default = () => { + const title = text( 'children', 'WordPress' ); + const href = text( 'href', 'https://wordpress.org' ); + + return <ExternalLink href={ href }>{ title }</ExternalLink>; +}; diff --git a/packages/components/src/font-size-picker/index.js b/packages/components/src/font-size-picker/index.js index a52f6ccd841d2f..9fc6567865dbe4 100644 --- a/packages/components/src/font-size-picker/index.js +++ b/packages/components/src/font-size-picker/index.js @@ -60,7 +60,7 @@ function FontSizePicker( { }; return ( - <fieldset> + <fieldset className="components-font-size-picker"> <legend> { __( 'Font Size' ) } </legend> diff --git a/packages/components/src/font-size-picker/style.scss b/packages/components/src/font-size-picker/style.scss index 2444087dcf30e3..d24d044952cdf6 100644 --- a/packages/components/src/font-size-picker/style.scss +++ b/packages/components/src/font-size-picker/style.scss @@ -38,3 +38,9 @@ } } +.components-font-size-picker { + border: 0; + padding: 0; + margin: 0; +} + diff --git a/packages/components/src/gradient-picker/index.js b/packages/components/src/gradient-picker/index.js index 1a60de751646f3..e8ab40f07ada2b 100644 --- a/packages/components/src/gradient-picker/index.js +++ b/packages/components/src/gradient-picker/index.js @@ -19,6 +19,7 @@ export default function GradientPicker( { gradients, onChange, value, + clearable = true, } ) { const clearGradient = useCallback( () => onChange( undefined ), @@ -56,7 +57,7 @@ export default function GradientPicker( { <CircularOptionPicker className={ className } options={ gradientOptions } - actions={ ( + actions={ clearable && ( <CircularOptionPicker.ButtonAction onClick={ clearGradient }> { __( 'Clear' ) } </CircularOptionPicker.ButtonAction> diff --git a/packages/components/src/higher-order/with-focus-return/index.js b/packages/components/src/higher-order/with-focus-return/index.js index 2b504878bdf528..6a411ad909af9f 100644 --- a/packages/components/src/higher-order/with-focus-return/index.js +++ b/packages/components/src/higher-order/with-focus-return/index.js @@ -36,11 +36,11 @@ function isComponentLike( object ) { * when the component is unmounted. * * @param {(WPComponent|Object)} options The component to be enhanced with - * focus return behavior, or an object - * describing the component and the - * focus return characteristics. + * focus return behavior, or an object + * describing the component and the + * focus return characteristics. * - * @return {Component} Component with the focus restauration behaviour. + * @return {WPComponent} Component with the focus restauration behaviour. */ function withFocusReturn( options ) { // Normalize as overloaded form `withFocusReturn( options )( Component )` diff --git a/packages/components/src/higher-order/with-notices/index.js b/packages/components/src/higher-order/with-notices/index.js index 49a44608039ce1..9097728b2921a8 100644 --- a/packages/components/src/higher-order/with-notices/index.js +++ b/packages/components/src/higher-order/with-notices/index.js @@ -17,8 +17,9 @@ import NoticeList from '../../notice/list'; /** * Override the default edit UI to include notices if supported. * - * @param {Function|Component} OriginalComponent Original component. - * @return {Component} Wrapped component. + * @param {WPComponent} OriginalComponent Original component. + * + * @return {WPComponent} Wrapped component. */ export default createHigherOrderComponent( ( OriginalComponent ) => { return class WrappedBlockEdit extends Component { diff --git a/packages/components/src/higher-order/with-spoken-messages/index.js b/packages/components/src/higher-order/with-spoken-messages/index.js index de17dc62aeb575..406d10bf0a245a 100644 --- a/packages/components/src/higher-order/with-spoken-messages/index.js +++ b/packages/components/src/higher-order/with-spoken-messages/index.js @@ -14,9 +14,9 @@ import { createHigherOrderComponent } from '@wordpress/compose'; * A Higher Order Component used to be provide a unique instance ID by * component. * - * @param {WPElement} WrappedComponent The wrapped component. + * @param {WPComponent} WrappedComponent The wrapped component. * - * @return {Component} Component with an instanceId prop. + * @return {WPComponent} The component to be rendered. */ export default createHigherOrderComponent( ( WrappedComponent ) => { return class extends Component { diff --git a/packages/components/src/icon-button/stories/index.js b/packages/components/src/icon-button/stories/index.js index d093876a20e0a4..b5c4275159dc5b 100644 --- a/packages/components/src/icon-button/stories/index.js +++ b/packages/components/src/icon-button/stories/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { text } from '@storybook/addon-knobs'; + /** * Internal dependencies */ @@ -5,7 +10,17 @@ import IconButton from '../'; export default { title: 'IconButton', component: IconButton }; -export const _default = () => <IconButton icon="ellipsis" label="More" />; +export const _default = () => { + const icon = text( 'Icon', 'ellipsis' ); + const label = text( 'Label', 'More' ); + + return ( + <IconButton + icon={ icon } + label={ label } + /> + ); +}; export const grouped = () => { const GroupContainer = ( { children } ) => ( diff --git a/packages/components/src/icon/stories/index.js b/packages/components/src/icon/stories/index.js index be4e9fed65787f..f2cd8bee1839b5 100644 --- a/packages/components/src/icon/stories/index.js +++ b/packages/components/src/icon/stories/index.js @@ -1,19 +1,29 @@ +/** + * External dependencies + */ +import { number, text } from '@storybook/addon-knobs'; + /** * Internal dependencies */ import Icon from '../'; -import { SVG, Path } from '../../'; +import { SVG, Path } from '../../primitives/svg'; export default { title: 'Icon', component: Icon }; const IconSizeLabel = ( { size } ) => <div style={ { fontSize: 12 } }>{ size }px</div>; -export const _default = () => ( - <div> - <Icon icon="screenoptions" /> - <IconSizeLabel size={ 24 } /> - </div> -); +export const _default = () => { + const icon = text( 'Icon', 'screenoptions' ); + const size = number( 'Size', '24' ); + + return ( + <div> + <Icon icon={ icon } size={ size } /> + <IconSizeLabel size={ size } /> + </div> + ); +}; export const sizes = () => { const iconSizes = [ 14, 16, 20, 24, 28, 32, 40, 48, 56 ]; diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 8542a86ba9b659..ac9694a49ba57b 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -12,6 +12,7 @@ export { default as ColorPalette } from './color-palette'; export { default as ColorPicker } from './color-picker'; export { default as Dashicon } from './dashicon'; export { DateTimePicker, DatePicker, TimePicker } from './date-time'; +export { default as __experimentalDimensionControl } from './dimension-control'; export { default as Disabled } from './disabled'; export { default as Draggable } from './draggable'; export { default as DropZone } from './drop-zone'; @@ -62,6 +63,7 @@ export { default as Toolbar } from './toolbar'; export { default as ToolbarButton } from './toolbar-button'; export { default as Tooltip } from './tooltip'; export { default as TreeSelect } from './tree-select'; +export { default as VisuallyHidden } from './visually-hidden'; export { default as IsolatedEventContainer } from './isolated-event-container'; export { createSlotFill, Slot, Fill, Provider as SlotFillProvider } from './slot-fill'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index c2c52ba08f0e58..e9aec0ad2fb71c 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -1,5 +1,7 @@ // Components export * from './primitives'; +export { default as ColorIndicator } from './color-indicator'; +export { default as ColorPalette } from './color-palette'; export { default as Dashicon } from './dashicon'; export { default as Dropdown } from './dropdown'; export { default as Toolbar } from './toolbar'; @@ -11,6 +13,7 @@ export { createSlotFill, Slot, Fill, Provider as SlotFillProvider } from './slot export { default as BaseControl } from './base-control'; export { default as TextareaControl } from './textarea-control'; export { default as PanelBody } from './panel/body'; +export { default as PanelActions } from './panel/actions'; export { default as Button } from './button'; export { default as TextControl } from './text-control'; export { default as ToggleControl } from './toggle-control'; diff --git a/packages/components/src/keyboard-shortcuts/index.native.js b/packages/components/src/keyboard-shortcuts/index.native.js new file mode 100644 index 00000000000000..fab7b99261e3ae --- /dev/null +++ b/packages/components/src/keyboard-shortcuts/index.native.js @@ -0,0 +1,2 @@ +const KeyboardShortcuts = () => null; +export default KeyboardShortcuts; diff --git a/packages/components/src/menu-item/index.js b/packages/components/src/menu-item/index.js index b7cc7c8ca7a0bd..3227659e6d10a8 100644 --- a/packages/components/src/menu-item/index.js +++ b/packages/components/src/menu-item/index.js @@ -18,7 +18,7 @@ import IconButton from '../icon-button'; /** * Renders a generic menu item for use inside the more menu. * - * @return {WPElement} More menu item. + * @return {WPComponent} The component to be rendered. */ export function MenuItem( { children, diff --git a/packages/components/src/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js index af74a25e2cee82..52ff00b6b5b856 100644 --- a/packages/components/src/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -42,6 +42,7 @@ class BottomSheetCell extends Component { value, valuePlaceholder = '', icon, + leftAlign, labelStyle = {}, valueStyle = {}, onChangeValue, @@ -57,8 +58,12 @@ class BottomSheetCell extends Component { const isValueEditable = editable && onChangeValue !== undefined; const cellLabelStyle = getStylesFromColorScheme( styles.cellLabel, styles.cellTextDark ); const cellLabelCenteredStyle = getStylesFromColorScheme( styles.cellLabelCentered, styles.cellTextDark ); - const defaultLabelStyle = showValue || icon !== undefined ? cellLabelStyle : cellLabelCenteredStyle; + const cellLabelLeftAlignNoIconStyle = getStylesFromColorScheme( styles.cellLabelLeftAlignNoIcon, styles.cellTextDark ); + const defaultMissingIconAndValue = leftAlign ? cellLabelLeftAlignNoIconStyle : cellLabelCenteredStyle; + const defaultLabelStyle = showValue || icon !== undefined ? cellLabelStyle : defaultMissingIconAndValue; + const drawSeparator = ( separatorType && separatorType !== 'none' ) || separatorStyle === undefined; + const drawTopSeparator = drawSeparator && separatorType === 'topFullWidth'; const onCellPress = () => { if ( isValueEditable ) { @@ -87,6 +92,7 @@ class BottomSheetCell extends Component { case 'leftMargin': return leftMarginStyle; case 'fullWidth': + case 'topFullWidth': return defaultSeparatorStyle; case 'none': return undefined; @@ -165,6 +171,9 @@ class BottomSheetCell extends Component { onPress={ onCellPress } style={ { ...styles.clipToBounds, ...style } } > + { drawTopSeparator && ( + <View style={ separatorStyle() } /> + ) } <View style={ styles.cellContainer }> <View style={ styles.cellRowContainer }> { icon && ( @@ -173,14 +182,14 @@ class BottomSheetCell extends Component { <View style={ platformStyles.labelIconSeparator } /> </View> ) } - <Text numberOfLines={ 1 } style={ { ...defaultLabelStyle, ...labelStyle } }> + <Text numberOfLines={ 1 } style={ [ defaultLabelStyle, labelStyle ] }> { label } </Text> </View> { showValue && getValueComponent() } { children } </View> - { drawSeparator && ( + { ! drawTopSeparator && ( <View style={ separatorStyle() } /> ) } </TouchableOpacity> diff --git a/packages/components/src/mobile/bottom-sheet/index.native.js b/packages/components/src/mobile/bottom-sheet/index.native.js index f17284b49e7306..34d05a83974957 100644 --- a/packages/components/src/mobile/bottom-sheet/index.native.js +++ b/packages/components/src/mobile/bottom-sheet/index.native.js @@ -19,6 +19,7 @@ import Button from './button'; import Cell from './cell'; import PickerCell from './picker-cell'; import SwitchCell from './switch-cell'; +import RangeCell from './range-cell'; import KeyboardAvoidingView from './keyboard-avoiding-view'; class BottomSheet extends Component { @@ -136,6 +137,8 @@ class BottomSheet extends Component { onBackdropPress={ this.props.onClose } onBackButtonPress={ this.props.onClose } onSwipe={ this.props.onClose } + onDismiss={ Platform.OS === 'ios' ? this.props.onDismiss : undefined } + onModalHide={ Platform.OS === 'android' ? this.props.onDismiss : undefined } swipeDirection="down" onMoveShouldSetResponder={ panResponder.panHandlers.onMoveShouldSetResponder } onMoveShouldSetResponderCapture={ panResponder.panHandlers.onMoveShouldSetResponderCapture } @@ -171,5 +174,6 @@ ThemedBottomSheet.Button = Button; ThemedBottomSheet.Cell = Cell; ThemedBottomSheet.PickerCell = PickerCell; ThemedBottomSheet.SwitchCell = SwitchCell; +ThemedBottomSheet.RangeCell = RangeCell; export default ThemedBottomSheet; diff --git a/packages/components/src/mobile/bottom-sheet/picker-cell.native.js b/packages/components/src/mobile/bottom-sheet/picker-cell.native.js index 50a775f25728b1..12eae94c7d8919 100644 --- a/packages/components/src/mobile/bottom-sheet/picker-cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/picker-cell.native.js @@ -7,6 +7,7 @@ import Picker from '../picker'; export default function BottomSheetPickerCell( props ) { const { options, + hideCancelButton, onChangeValue, ...cellProps } = props; @@ -24,6 +25,8 @@ export default function BottomSheetPickerCell( props ) { return ( <Cell onPress={ onCellPress } editable={ false } { ...cellProps } > <Picker + leftAlign + hideCancelButton={ hideCancelButton } ref={ ( instance ) => picker = instance } options={ options } onChange={ onChange } diff --git a/packages/components/src/mobile/bottom-sheet/range-cell.native.js b/packages/components/src/mobile/bottom-sheet/range-cell.native.js new file mode 100644 index 00000000000000..07b343e9ec0781 --- /dev/null +++ b/packages/components/src/mobile/bottom-sheet/range-cell.native.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import { Platform } from 'react-native'; + +/** + * Internal dependencies + */ +import Cell from './cell'; +import Slider from '../slider'; + +export default function BottomSheetRangeCell( props ) { + const { + value, + defaultValue, + onChangeValue, + minimumValue = 0, + maximumValue = 10, + disabled, + step = 1, + minimumTrackTintColor = '#00669b', + maximumTrackTintColor = Platform.OS === 'ios' ? '#e9eff3' : '#909090', + thumbTintColor = Platform.OS === 'ios' ? '#fff' : '#00669b', + ...cellProps + } = props; + + return ( + <Cell + editable={ false } + { ...cellProps } + > + <Slider + value={ value } + defaultValue={ defaultValue } + disabled={ disabled } + step={ step } + minimumValue={ minimumValue } + maximumValue={ maximumValue } + minimumTrackTintColor={ minimumTrackTintColor } + maximumTrackTintColor={ maximumTrackTintColor } + thumbTintColor={ thumbTintColor } + onChangeValue={ onChangeValue } + /> + </Cell> + ); +} diff --git a/packages/components/src/mobile/bottom-sheet/styles.native.scss b/packages/components/src/mobile/bottom-sheet/styles.native.scss index 8f153715c16705..86422f3228cac4 100644 --- a/packages/components/src/mobile/bottom-sheet/styles.native.scss +++ b/packages/components/src/mobile/bottom-sheet/styles.native.scss @@ -120,6 +120,13 @@ text-align: center; } +.cellLabelLeftAlignNoIcon { + font-size: 17px; + color: #2e4453; + flex: 1; + margin-left: 12px; +} + .cellValue { font-size: 17px; color: #2e4453; diff --git a/packages/components/src/mobile/picker/index.android.js b/packages/components/src/mobile/picker/index.android.js index 3fad1cc2b23bf0..372378e0a2b3ed 100644 --- a/packages/components/src/mobile/picker/index.android.js +++ b/packages/components/src/mobile/picker/index.android.js @@ -52,6 +52,7 @@ export default class Picker extends Component { <BottomSheet.Cell icon={ option.icon } key={ index } + leftAlign={ this.props.leftAlign } label={ option.label } separatorType={ 'none' } onPress={ () => this.onCellPress( option.value ) } diff --git a/packages/components/src/mobile/picker/index.ios.js b/packages/components/src/mobile/picker/index.ios.js index 2cf9798b28e9c0..9de17d1ff7060a 100644 --- a/packages/components/src/mobile/picker/index.ios.js +++ b/packages/components/src/mobile/picker/index.ios.js @@ -11,12 +11,13 @@ import { Component } from '@wordpress/element'; class Picker extends Component { presentPicker() { - const { options, onChange } = this.props; + const { options, onChange, title } = this.props; const labels = options.map( ( { label } ) => label ); const fullOptions = [ __( 'Cancel' ) ].concat( labels ); ActionSheetIOS.showActionSheetWithOptions( { + title, options: fullOptions, cancelButtonIndex: 0, }, diff --git a/packages/components/src/mobile/slider/index.native.js b/packages/components/src/mobile/slider/index.native.js new file mode 100644 index 00000000000000..111d9987e973db --- /dev/null +++ b/packages/components/src/mobile/slider/index.native.js @@ -0,0 +1,118 @@ +/** + * External dependencies + */ +import { Slider as RNSlider, TextInput, View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import styles from './styles.scss'; + +class Slider extends Component { + constructor( props ) { + super( props ); + this.handleToggleFocus = this.handleToggleFocus.bind( this ); + this.handleChange = this.handleChange.bind( this ); + this.handleValueSave = this.handleValueSave.bind( this ); + this.handleReset = this.handleReset.bind( this ); + + const initialValue = this.validateInput( props.value || props.defaultValue || props.minimumValue ); + + this.state = { hasFocus: false, initialValue, sliderValue: initialValue }; + } + + componentDidUpdate( ) { + const reset = this.props.value === null; + if ( reset ) { + this.handleReset(); + } + } + + handleToggleFocus( validateInput = true ) { + const newState = { hasFocus: ! this.state.hasFocus }; + + if ( validateInput ) { + const sliderValue = this.validateInput( this.state.sliderValue ); + this.handleValueSave( sliderValue ); + } + + this.setState( newState ); + } + + validateInput( text ) { + const { minimumValue, maximumValue } = this.props; + if ( ! text ) { + return minimumValue; + } + if ( typeof text === 'number' ) { + return Math.min( Math.max( text, minimumValue ), maximumValue ); + } + return Math.min( Math.max( text.replace( /[^0-9]/g, '' ).replace( /^0+(?=\d)/, '' ), minimumValue ), maximumValue ); + } + + handleChange( text ) { + if ( ! isNaN( Number( text ) ) ) { + this.setState( { sliderValue: text } ); + } + } + + handleValueSave( text ) { + if ( ! isNaN( Number( text ) ) ) { + if ( this.props.onChangeValue ) { + this.props.onChangeValue( text ); + } + this.setState( { sliderValue: text } ); + } + } + + handleReset() { + this.handleValueSave( this.props.defaultValue || this.state.initialValue ); + } + + render() { + const { + minimumValue, + maximumValue, + disabled, + step, + minimumTrackTintColor, + maximumTrackTintColor, + thumbTintColor, + } = this.props; + + const { hasFocus, sliderValue } = this.state; + + return ( + <View style={ styles.sliderContainer }> + <RNSlider + value={ this.validateInput( sliderValue ) } + disabled={ disabled } + style={ styles.slider } + step={ step } + minimumValue={ minimumValue } + maximumValue={ maximumValue } + minimumTrackTintColor={ minimumTrackTintColor } + maximumTrackTintColor={ maximumTrackTintColor } + thumbTintColor={ thumbTintColor } + onValueChange={ this.handleChange } + onSlidingComplete={ this.handleValueSave } + /> + <TextInput + style={ [ styles.sliderTextInput, hasFocus ? styles.isSelected : {} ] } + onChangeText={ this.handleChange } + onFocus={ this.handleToggleFocus } + onBlur={ this.handleToggleFocus } + keyboardType="numeric" + value={ `${ sliderValue }` } + /> + </View> + ); + } +} + +export default Slider; diff --git a/packages/components/src/mobile/slider/styles.scss b/packages/components/src/mobile/slider/styles.scss new file mode 100644 index 00000000000000..326880b621807a --- /dev/null +++ b/packages/components/src/mobile/slider/styles.scss @@ -0,0 +1,27 @@ +.sliderContainer { + flex: 1; + flex-direction: row; + align-content: center; + justify-content: space-evenly; +} + +.slider { + flex-grow: 1; +} + +.sliderTextInput { + width: 40px; + height: 25px; + align-self: center; + margin-left: 10px; + border-width: 1px; + border-radius: 4px; + border-color: $dark-gray-150; + padding-top: 0; + padding-bottom: 0; +} + +.isSelected { + border-width: 2px; + border-color: $blue-wordpress; +} diff --git a/packages/components/src/modal/index.js b/packages/components/src/modal/index.js index 66d3421ee6ff5c..f4a64c289b4e7d 100644 --- a/packages/components/src/modal/index.js +++ b/packages/components/src/modal/index.js @@ -3,7 +3,7 @@ */ import { Component, createPortal } from '@wordpress/element'; import { withInstanceId } from '@wordpress/compose'; -import { deprecated } from '@wordpress/deprecated'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies diff --git a/packages/components/src/panel/actions.native.js b/packages/components/src/panel/actions.native.js new file mode 100644 index 00000000000000..02031bf53cb857 --- /dev/null +++ b/packages/components/src/panel/actions.native.js @@ -0,0 +1,36 @@ +/** + * External dependencies + */ +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { + TextControl, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import styles from './actions.scss'; + +function PanelActions( { actions } ) { + return ( + <View style={ styles.panelActionsContainer }> + { actions.map( ( { label, onPress } ) => { + return ( + <TextControl + label={ label } + separatorType="topFullWidth" + onPress={ onPress } + labelStyle={ styles.defaultLabelStyle } + key={ label } + /> + ); + } ) } + </View> + ); +} + +export default PanelActions; diff --git a/packages/components/src/panel/actions.native.scss b/packages/components/src/panel/actions.native.scss new file mode 100644 index 00000000000000..ed226c13883be9 --- /dev/null +++ b/packages/components/src/panel/actions.native.scss @@ -0,0 +1,7 @@ +.panelActionsContainer { + padding-top: 24; +} + +.defaultLabelStyle { + color: $alert-red; +} diff --git a/packages/components/src/panel/body.native.js b/packages/components/src/panel/body.native.js index aeeccc77a06c19..314650f945278e 100644 --- a/packages/components/src/panel/body.native.js +++ b/packages/components/src/panel/body.native.js @@ -1,12 +1,16 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { Text, View } from 'react-native'; /** * WordPress dependencies */ import { Component } from '@wordpress/element'; +/** + * Internal dependencies + */ +import styles from './body.scss'; export class PanelBody extends Component { constructor( ) { @@ -15,9 +19,11 @@ export class PanelBody extends Component { } render() { - const { children } = this.props; + const { children, title } = this.props; + return ( - <View > + <View style={ styles.panelContainer }> + { title && <Text style={ styles.sectionHeaderText }>{ title }</Text> } { children } </View> ); diff --git a/packages/components/src/panel/body.native.scss b/packages/components/src/panel/body.native.scss new file mode 100644 index 00000000000000..a30c4df4c15dfc --- /dev/null +++ b/packages/components/src/panel/body.native.scss @@ -0,0 +1,11 @@ +.panelContainer { + padding: 0 16px; +} + +.sectionHeaderText { + color: #87a6bc; + padding-top: 24; + padding-bottom: 8; + font-size: 14; + font-weight: 500; +} diff --git a/packages/components/src/placeholder/README.md b/packages/components/src/placeholder/README.md index 2a12363e7fde01..9e5bf053417d0d 100644 --- a/packages/components/src/placeholder/README.md +++ b/packages/components/src/placeholder/README.md @@ -16,7 +16,7 @@ const MyPlaceholder = () => ( Name | Type | Default | Description --- | --- | --- | --- -`icon` | `string, ReactElement` | `undefined` | If provided, renders an icon next to the label. +`icon` | `string, WPElement` | `undefined` | If provided, renders an icon next to the label. `label` | `string` | `undefined` | Renders a label for the placeholder. `instructions` | `string` | `undefined` | Renders instruction text below label. `isColumnLayout` | `bool` | `false` | Changes placeholder children layout from flex-row to flex-column. diff --git a/packages/components/src/scroll-lock/index.js b/packages/components/src/scroll-lock/index.js index 0d600aac62f14d..1d6255d8c12ef9 100644 --- a/packages/components/src/scroll-lock/index.js +++ b/packages/components/src/scroll-lock/index.js @@ -12,7 +12,7 @@ import { Component } from '@wordpress/element'; * @param {Object} args Keyword args. * @param {HTMLDocument} args.htmlDocument The document to lock the scroll for. * @param {string} args.className The name of the class used to lock scrolling. - * @return {Component} The bound ScrollLock component. + * @return {WPComponent} The bound ScrollLock component. */ export function createScrollLockComponent( { htmlDocument = document, diff --git a/packages/components/src/scroll-lock/stories/index.js b/packages/components/src/scroll-lock/stories/index.js index 865fb6a7e909b9..20ef27337fc787 100644 --- a/packages/components/src/scroll-lock/stories/index.js +++ b/packages/components/src/scroll-lock/stories/index.js @@ -6,7 +6,7 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import { Button } from '../../'; +import Button from '../../button'; import ScrollLock from '../'; export default { title: 'ScrollLock', component: ScrollLock }; diff --git a/packages/components/src/spinner/stories/index.js b/packages/components/src/spinner/stories/index.js new file mode 100644 index 00000000000000..188f13e45273f4 --- /dev/null +++ b/packages/components/src/spinner/stories/index.js @@ -0,0 +1,12 @@ +/** + * Internal dependencies + */ +import Spinner from '../'; + +export default { title: 'Spinner', component: Spinner }; + +export const _default = () => { + return ( + <Spinner /> + ); +}; diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 6365bff68cf568..7a62ba0534539d 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -9,6 +9,7 @@ @import "./color-picker/style.scss"; @import "./dashicon/style.scss"; @import "./date-time/style.scss"; +@import "./dimension-control/style.scss"; @import "./disabled/style.scss"; @import "./draggable/style.scss"; @import "./drop-zone/style.scss"; @@ -44,3 +45,4 @@ @import "./toolbar/style.scss"; @import "./toolbar-button/style.scss"; @import "./tooltip/style.scss"; +@import "./visually-hidden/style.scss"; diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index ca41d32bb6e1b9..bf9237eed1b624 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -34,13 +34,13 @@ import ToolbarContainer from './toolbar-container'; * Either `controls` or `children` is required, otherwise this components * renders nothing. * - * @param {Object} props - * @param {Array} [props.controls] The controls to render in this toolbar. - * @param {ReactElement} [props.children] Any other things to render inside the - * toolbar besides the controls. - * @param {string} [props.className] Class to set on the container div. + * @param {Object} props + * @param {Array} [props.controls] The controls to render in this toolbar. + * @param {WPElement} [props.children] Any other things to render inside the + * toolbar besides the controls. + * @param {string} [props.className] Class to set on the container div. * - * @return {ReactElement} The rendered toolbar. + * @return {WPComponent} The rendered component. */ function Toolbar( { controls = [], children, className, isCollapsed, icon, label, ...otherProps } ) { if ( diff --git a/packages/components/src/visually-hidden/README.md b/packages/components/src/visually-hidden/README.md new file mode 100644 index 00000000000000..1f490f5134af26 --- /dev/null +++ b/packages/components/src/visually-hidden/README.md @@ -0,0 +1,9 @@ +# VisuallyHidden + +A component used to render text intended to be visually hidden, but will show for alternate devices, for example a screen reader. + +### Usage + +```jsx +<VisuallyHidden> Show text for screenreader. </VisuallyHidden> +``` diff --git a/packages/components/src/visually-hidden/index.js b/packages/components/src/visually-hidden/index.js new file mode 100644 index 00000000000000..b29c954c2cd9e1 --- /dev/null +++ b/packages/components/src/visually-hidden/index.js @@ -0,0 +1,22 @@ + +/** + * Internal dependencies + */ +import { renderAsRenderProps } from './utils'; + +/** + * VisuallyHidden component to render text out non-visually + * for use in devices such as a screen reader. + */ +function VisuallyHidden( { + as = 'div', + ...props +} ) { + return renderAsRenderProps( { + as, + className: 'components-visually-hidden', + ...props, + } ); +} +export default VisuallyHidden; + diff --git a/packages/components/src/visually-hidden/stories/index.js b/packages/components/src/visually-hidden/stories/index.js new file mode 100644 index 00000000000000..03b60ccc334126 --- /dev/null +++ b/packages/components/src/visually-hidden/stories/index.js @@ -0,0 +1,17 @@ +/** + * Internal dependencies + */ +import VisuallyHidden from '../'; + +export default { title: 'VisuallyHidden', component: VisuallyHidden }; + +export const _default = () => ( + <> + <VisuallyHidden> + This should not show. + </VisuallyHidden> + <div> + This text will <VisuallyHidden as="span">but not inline </VisuallyHidden> always show. + </div> + </> +); diff --git a/packages/components/src/visually-hidden/style.scss b/packages/components/src/visually-hidden/style.scss new file mode 100644 index 00000000000000..02fec4486afa23 --- /dev/null +++ b/packages/components/src/visually-hidden/style.scss @@ -0,0 +1,30 @@ +.components-visually-hidden { + border: 0; + clip: rect(1px, 1px, 1px, 1px); + -webkit-clip-path: inset(50%); + clip-path: inset(50%); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + word-wrap: normal !important; +} + +.components-visually-hidden:focus { + background-color: $light-gray-500; + clip: auto !important; + clip-path: none; + color: #444; + display: block; + font-size: 1em; + height: auto; + left: 5px; + line-height: normal; + padding: 15px 23px 14px; + text-decoration: none; + top: 5px; + width: auto; + z-index: 100000; +} diff --git a/packages/components/src/visually-hidden/utils.js b/packages/components/src/visually-hidden/utils.js new file mode 100644 index 00000000000000..07957708090ef0 --- /dev/null +++ b/packages/components/src/visually-hidden/utils.js @@ -0,0 +1,22 @@ +/** + * Utility Functions + */ + +/** + * renderAsRenderProps is used to wrap a component and convert + * the passed property "as" either a string or component, to the + * rendered tag if a string, or component. + * + * See VisuallyHidden hidden for example. + * + * @param {string|Component} as A tag or component to render. + * @return {Component} The rendered component. + */ +function renderAsRenderProps( { as: Component = 'div', ...props } ) { + if ( typeof props.children === 'function' ) { + return props.children( props ); + } + return <Component { ...props } />; +} + +export { renderAsRenderProps }; diff --git a/packages/components/storybook/addons.js b/packages/components/storybook/addons.js index d3fa048c75edf4..ccfec56c606dcc 100644 --- a/packages/components/storybook/addons.js +++ b/packages/components/storybook/addons.js @@ -2,5 +2,6 @@ * External dependencies */ import '@storybook/addon-a11y/register'; +import '@storybook/addon-knobs/register'; import '@storybook/addon-storysource/register'; import '@storybook/addon-viewport/register'; diff --git a/packages/components/storybook/config.js b/packages/components/storybook/config.js index eace300f3fc963..b2bfd74c3da6eb 100644 --- a/packages/components/storybook/config.js +++ b/packages/components/storybook/config.js @@ -3,6 +3,7 @@ */ import { addDecorator, configure } from '@storybook/react'; import { withA11y } from '@storybook/addon-a11y'; +import { withKnobs } from '@storybook/addon-knobs'; /** * Internal dependencies @@ -10,6 +11,7 @@ import { withA11y } from '@storybook/addon-a11y'; import '../build-style/style.css'; addDecorator( withA11y ); +addDecorator( withKnobs ); configure( [ require.context( '../docs', true, /\/.+\.mdx$/ ), diff --git a/packages/compose/README.md b/packages/compose/README.md index dc47f26e995652..7fcf12e07a7293 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -163,11 +163,11 @@ component. _Parameters_ -- _WrappedComponent_ `WPElement`: The wrapped component. +- _WrappedComponent_ `WPComponent`: The wrapped component. _Returns_ -- `Component`: Component with an instanceId prop. +- `WPComponent`: Component with an instanceId prop. <a name="withSafeTimeout" href="#withSafeTimeout">#</a> **withSafeTimeout** @@ -176,11 +176,11 @@ that ought to be bound to a component's lifecycle. _Parameters_ -- _OriginalComponent_ `Component`: Component requiring setTimeout +- _OriginalComponent_ `WPComponent`: Component requiring setTimeout _Returns_ -- `Component`: Wrapped component. +- `WPComponent`: Wrapped component. <a name="withState" href="#withState">#</a> **withState** @@ -193,7 +193,7 @@ _Parameters_ _Returns_ -- `Component`: Wrapped component. +- `WPComponent`: Wrapped component. <!-- END TOKEN(Autogenerated API docs) --> diff --git a/packages/compose/package.json b/packages/compose/package.json index 827a0afb46eff7..5ad5ed9d1bc786 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "3.7.0", + "version": "3.7.2", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -21,6 +21,7 @@ "main": "build/index.js", "module": "build-module/index.js", "react-native": "src/index", + "sideEffects": false, "dependencies": { "@babel/runtime": "^7.4.4", "@wordpress/element": "file:../element", diff --git a/packages/compose/src/higher-order/compose.js b/packages/compose/src/higher-order/compose.js new file mode 100644 index 00000000000000..a166387821e7b3 --- /dev/null +++ b/packages/compose/src/higher-order/compose.js @@ -0,0 +1,14 @@ +/** + * External dependencies + */ +import { flowRight as compose } from 'lodash'; + +/** + * Composes multiple higher-order components into a single higher-order component. Performs right-to-left function + * composition, where each successive invocation is supplied the return value of the previous. + * + * @param {...Function} hocs The HOC functions to invoke. + * + * @return {Function} Returns the new composite function. + */ +export default compose; diff --git a/packages/compose/src/higher-order/with-instance-id/index.js b/packages/compose/src/higher-order/with-instance-id/index.js index 53c09ec56f8882..960c6997e8671f 100644 --- a/packages/compose/src/higher-order/with-instance-id/index.js +++ b/packages/compose/src/higher-order/with-instance-id/index.js @@ -12,9 +12,9 @@ import createHigherOrderComponent from '../../utils/create-higher-order-componen * A Higher Order Component used to be provide a unique instance ID by * component. * - * @param {WPElement} WrappedComponent The wrapped component. + * @param {WPComponent} WrappedComponent The wrapped component. * - * @return {Component} Component with an instanceId prop. + * @return {WPComponent} Component with an instanceId prop. */ export default createHigherOrderComponent( ( WrappedComponent ) => { let instances = 0; diff --git a/packages/compose/src/higher-order/with-safe-timeout/index.js b/packages/compose/src/higher-order/with-safe-timeout/index.js index 910dd94fddc19d..43d4e9e780d5b7 100644 --- a/packages/compose/src/higher-order/with-safe-timeout/index.js +++ b/packages/compose/src/higher-order/with-safe-timeout/index.js @@ -17,9 +17,9 @@ import createHigherOrderComponent from '../../utils/create-higher-order-componen * A higher-order component used to provide and manage delayed function calls * that ought to be bound to a component's lifecycle. * - * @param {Component} OriginalComponent Component requiring setTimeout + * @param {WPComponent} OriginalComponent Component requiring setTimeout * - * @return {Component} Wrapped component. + * @return {WPComponent} Wrapped component. */ const withSafeTimeout = createHigherOrderComponent( ( OriginalComponent ) => { diff --git a/packages/compose/src/higher-order/with-state/index.js b/packages/compose/src/higher-order/with-state/index.js index 19e639043bd28b..02db9392b39631 100644 --- a/packages/compose/src/higher-order/with-state/index.js +++ b/packages/compose/src/higher-order/with-state/index.js @@ -14,7 +14,7 @@ import createHigherOrderComponent from '../../utils/create-higher-order-componen * * @param {?Object} initialState Optional initial state of the component. * - * @return {Component} Wrapped component. + * @return {WPComponent} Wrapped component. */ export default function withState( initialState = {} ) { return createHigherOrderComponent( ( OriginalComponent ) => { diff --git a/packages/compose/src/index.js b/packages/compose/src/index.js index 9ea31b2d351aba..ab1ecf3564edbe 100644 --- a/packages/compose/src/index.js +++ b/packages/compose/src/index.js @@ -1,20 +1,8 @@ -/** - * External dependencies - */ -import { flowRight } from 'lodash'; - // Utils export { default as createHigherOrderComponent } from './utils/create-higher-order-component'; -/** - * Composes multiple higher-order components into a single higher-order component. Performs right-to-left function - * composition, where each successive invocation is supplied the return value of the previous. - * - * @param {...Function} hocs The HOC functions to invoke. - * - * @return {Function} Returns the new composite function. - */ -export { flowRight as compose }; +// Compose helper (aliased flowRight from Lodash) +export { default as compose } from './higher-order/compose'; // Higher-order components export { default as ifCondition } from './higher-order/if-condition'; diff --git a/packages/core-data/README.md b/packages/core-data/README.md index 892a6e572a99be..a2bb3ccbe6deeb 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -106,7 +106,7 @@ a given URl has been received. _Parameters_ - _url_ `string`: URL to preview the embed for. -- _preview_ `Mixed`: Preview data. +- _preview_ `*`: Preview data. _Returns_ diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 32fca25d8a2771..3c565d2bfc56c6 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "2.7.0", + "version": "2.7.3", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index dc77d54994ac0b..a79aaba7e33d8f 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -109,8 +109,8 @@ export function receiveThemeSupports( themeSupports ) { * Returns an action object used in signalling that the preview data for * a given URl has been received. * - * @param {string} url URL to preview the embed for. - * @param {Mixed} preview Preview data. + * @param {string} url URL to preview the embed for. + * @param {*} preview Preview data. * * @return {Object} Action object. */ diff --git a/packages/core-data/src/entities.js b/packages/core-data/src/entities.js index 4ee2f6b715374f..b75d0e44632373 100644 --- a/packages/core-data/src/entities.js +++ b/packages/core-data/src/entities.js @@ -12,6 +12,7 @@ import { apiFetch, select } from './controls'; export const DEFAULT_ENTITY_KEY = 'id'; export const defaultEntities = [ + { name: 'site', kind: 'root', baseURL: '/wp/v2/settings' }, { name: 'postType', kind: 'root', key: 'slug', baseURL: '/wp/v2/types' }, { name: 'media', kind: 'root', baseURL: '/wp/v2/media', plural: 'mediaItems' }, { name: 'taxonomy', kind: 'root', key: 'slug', baseURL: '/wp/v2/taxonomies', plural: 'taxonomies' }, diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js index 7870e0217cc974..50439f1b57ad65 100644 --- a/packages/core-data/src/entity-provider.js +++ b/packages/core-data/src/entity-provider.js @@ -52,6 +52,17 @@ export default function EntityProvider( { kind, type, id, children } ) { return <Provider value={ id }>{ children }</Provider>; } +/** + * Hook that returns the ID for the nearest + * provided entity of the specified type. + * + * @param {string} kind The entity kind. + * @param {string} type The entity type. + */ +export function useEntityId( kind, type ) { + return useContext( getEntity( kind, type ).context ); +} + /** * Hook that returns the value and a setter for the * specified property of the nearest provided @@ -66,11 +77,13 @@ export default function EntityProvider( { kind, type, id, children } ) { * setter. */ export function useEntityProp( kind, type, prop ) { - const id = useContext( getEntity( kind, type ).context ); + const id = useEntityId( kind, type ); const value = useSelect( ( select ) => { - const entity = select( 'core' ).getEditedEntityRecord( kind, type, id ); + const { getEntityRecord, getEditedEntityRecord } = select( 'core' ); + getEntityRecord( kind, type, id ); // Trigger resolver. + const entity = getEditedEntityRecord( kind, type, id ); return entity && entity[ prop ]; }, [ kind, type, id, prop ] @@ -88,3 +101,62 @@ export function useEntityProp( kind, type, prop ) { return [ value, setValue ]; } + +/** + * Hook that returns whether the nearest provided + * entity of the specified type is dirty, saving, + * and a function to save it. + * + * The last, optional parameter is for scoping the + * selection to a single property or a list properties. + * + * By default, dirtyness detection and saving considers + * and handles all properties of an entity, but this + * last parameter lets you scope it to a single property + * or a list of properties for each instance of this hook. + * + * @param {string} kind The entity kind. + * @param {string} type The entity type. + * @param {string|[string]} [props] The property name or list of property names. + */ +export function __experimentalUseEntitySaving( kind, type, props ) { + const id = useEntityId( kind, type ); + + const [ isDirty, isSaving, edits ] = useSelect( + ( select ) => { + const { getEntityRecordNonTransientEdits, isSavingEntityRecord } = select( + 'core' + ); + const _edits = getEntityRecordNonTransientEdits( kind, type, id ); + const editKeys = Object.keys( _edits ); + return [ + props ? + editKeys.some( ( key ) => + typeof props === 'string' ? key === props : props.includes( key ) + ) : + editKeys.length > 0, + isSavingEntityRecord( kind, type, id ), + _edits, + ]; + }, + [ kind, type, id, props ] + ); + + const { saveEntityRecord } = useDispatch( 'core' ); + const save = useCallback( () => { + let filteredEdits = edits; + if ( typeof props === 'string' ) { + filteredEdits = { [ props ]: filteredEdits[ props ] }; + } else if ( props ) { + filteredEdits = filteredEdits.reduce( ( acc, key ) => { + if ( props.includes( key ) ) { + acc[ key ] = filteredEdits[ key ]; + } + return acc; + }, {} ); + } + saveEntityRecord( kind, type, { id, ...filteredEdits } ); + }, [ kind, type, id, props, edits ] ); + + return [ isDirty, isSaving, save ]; +} diff --git a/packages/core-data/src/index.js b/packages/core-data/src/index.js index 2cdddb960e448b..bf12ac2e5096fb 100644 --- a/packages/core-data/src/index.js +++ b/packages/core-data/src/index.js @@ -49,4 +49,9 @@ registerStore( REDUCER_KEY, { resolvers: { ...resolvers, ...entityResolvers }, } ); -export { default as EntityProvider, useEntityProp } from './entity-provider'; +export { + default as EntityProvider, + useEntityId, + useEntityProp, + __experimentalUseEntitySaving, +} from './entity-provider'; diff --git a/packages/core-data/src/queried-data/reducer.js b/packages/core-data/src/queried-data/reducer.js index 0c3256e9a8f987..36da4465bb922d 100644 --- a/packages/core-data/src/queried-data/reducer.js +++ b/packages/core-data/src/queried-data/reducer.js @@ -74,10 +74,10 @@ function items( state = {}, action ) { const key = action.key || DEFAULT_ENTITY_KEY; return { ...state, - ...action.items.reduce( ( acc, value ) => { + ...action.items.reduce( ( accumulator, value ) => { const itemId = value[ key ]; - acc[ itemId ] = conservativeMapItem( state[ itemId ], value ); - return acc; + accumulator[ itemId ] = conservativeMapItem( state[ itemId ], value ); + return accumulator; }, {} ), }; } diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 8edfdbf895cded..670cb4adf2a750 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -47,7 +47,7 @@ export function* getCurrentUser() { * @param {string} name Entity name. * @param {number} key Record's key */ -export function* getEntityRecord( kind, name, key ) { +export function* getEntityRecord( kind, name, key = '' ) { const entities = yield getKindEntities( kind ); const entity = find( entities, { kind, name } ); if ( ! entity ) { diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 4c61d59220843f..3974d66c83b60b 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -123,12 +123,12 @@ export const getRawEntityRecord = createSelector( const record = getEntityRecord( state, kind, name, key ); return ( record && - Object.keys( record ).reduce( ( acc, _key ) => { + Object.keys( record ).reduce( ( accumulator, _key ) => { // Because edits are the "raw" attribute values, // we return those from record selectors to make rendering, // comparisons, and joins with edits easier. - acc[ _key ] = get( record[ _key ], 'raw', record[ _key ] ); - return acc; + accumulator[ _key ] = get( record[ _key ], 'raw', record[ _key ] ); + return accumulator; }, {} ) ); }, diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index 8e74ef5a768b4c..9b6d2724b43e45 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "1.3.0", + "version": "1.3.3", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/README.md b/packages/data/README.md index a76019371e725a..68dc1a48e1697a 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -251,6 +251,46 @@ Specific implementation differences from Redux and React Redux: <!-- START TOKEN(Autogenerated API docs) --> +<a name="AsyncModeProvider" href="#AsyncModeProvider">#</a> **AsyncModeProvider** + +Context Provider Component used to switch the data module component rerendering +between Sync and Async modes. + +_Usage_ + +```js +import { useSelect, AsyncModeProvider } from '@wordpress/data'; + +function BlockCount() { + const count = useSelect( ( select ) => { + return select( 'core/block-editor' ).getBlockCount() + } ); + + return count; +} + +function App() { + return ( + <AsyncModeProvider value={ true }> + <BlockCount /> + </AsyncModeProvider> + ); +} +``` + +In this example, the BlockCount component is rerendered asynchronously. +It means if a more critical task is being performed (like typing in an input), +the rerendering is delayed until the browser becomes IDLE. +It is possible to nest multiple levels of AsyncModeProvider to fine-tune the rendering behavior. + +_Parameters_ + +- _props.value_ `boolean`: Enable Async Mode. + +_Returns_ + +- `WPComponent`: The component to be rendered. + <a name="combineReducers" href="#combineReducers">#</a> **combineReducers** The combineReducers helper function turns an object whose values are different @@ -693,7 +733,7 @@ _Parameters_ _Returns_ -- `Component`: Enhanced component with merged dispatcher props. +- `WPComponent`: Enhanced component with merged dispatcher props. <a name="withRegistry" href="#withRegistry">#</a> **withRegistry** @@ -750,7 +790,7 @@ _Parameters_ _Returns_ -- `Component`: Enhanced component with merged state data props. +- `WPComponent`: Enhanced component with merged state data props. <!-- END TOKEN(Autogenerated API docs) --> diff --git a/packages/data/package.json b/packages/data/package.json index 9eb21a7608146d..3812312f7dad8d 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "4.9.0", + "version": "4.9.2", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/src/components/async-mode-provider/context.js b/packages/data/src/components/async-mode-provider/context.js index c0e59ef7138183..88c98cb260b4a0 100644 --- a/packages/data/src/components/async-mode-provider/context.js +++ b/packages/data/src/components/async-mode-provider/context.js @@ -9,4 +9,38 @@ const { Consumer, Provider } = Context; export const AsyncModeConsumer = Consumer; +/** + * Context Provider Component used to switch the data module component rerendering + * between Sync and Async modes. + * + * @example + * + * ```js + * import { useSelect, AsyncModeProvider } from '@wordpress/data'; + * + * function BlockCount() { + * const count = useSelect( ( select ) => { + * return select( 'core/block-editor' ).getBlockCount() + * } ); + * + * return count; + * } + * + * function App() { + * return ( + * <AsyncModeProvider value={ true }> + * <BlockCount /> + * </AsyncModeProvider> + * ); + * } + * ``` + * + * In this example, the BlockCount component is rerendered asynchronously. + * It means if a more critical task is being performed (like typing in an input), + * the rerendering is delayed until the browser becomes IDLE. + * It is possible to nest multiple levels of AsyncModeProvider to fine-tune the rendering behavior. + * + * @param {boolean} props.value Enable Async Mode. + * @return {WPComponent} The component to be rendered. + */ export default Provider; diff --git a/packages/data/src/components/with-dispatch/index.js b/packages/data/src/components/with-dispatch/index.js index 2247a253a189d5..3e6c2194dec5f4 100644 --- a/packages/data/src/components/with-dispatch/index.js +++ b/packages/data/src/components/with-dispatch/index.js @@ -85,7 +85,7 @@ import { useDispatchWithMap } from '../use-dispatch'; * returns an object with the same keys. For example, it should not contain * conditions under which a different value would be returned. * - * @return {Component} Enhanced component with merged dispatcher props. + * @return {WPComponent} Enhanced component with merged dispatcher props. */ const withDispatch = ( mapDispatchToProps ) => createHigherOrderComponent( ( WrappedComponent ) => ( ownProps ) => { diff --git a/packages/data/src/components/with-select/index.js b/packages/data/src/components/with-select/index.js index f9659e7e865a8f..aeb86761494e44 100644 --- a/packages/data/src/components/with-select/index.js +++ b/packages/data/src/components/with-select/index.js @@ -45,7 +45,7 @@ import useSelect from '../use-select'; * component and update automatically if the price of a hammer ever changes in * the store. * - * @return {Component} Enhanced component with merged state data props. + * @return {WPComponent} Enhanced component with merged state data props. */ const withSelect = ( mapSelectToProps ) => createHigherOrderComponent( ( WrappedComponent ) => pure( diff --git a/packages/data/src/factory.js b/packages/data/src/factory.js index 2a10c7a8e7afc1..bdc2f1519e60e5 100644 --- a/packages/data/src/factory.js +++ b/packages/data/src/factory.js @@ -3,10 +3,6 @@ */ import defaultRegistry from './default-registry'; -/** - * @typedef {import('./registry').WPDataRegistry} WPDataRegistry - */ - /** * Mark a selector as a registry selector. * diff --git a/packages/data/src/index.js b/packages/data/src/index.js index 8dd6893c5d9e01..c7ad8f67a7484d 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -19,9 +19,7 @@ export { } from './components/registry-provider'; export { default as useSelect } from './components/use-select'; export { useDispatch } from './components/use-dispatch'; -export { - AsyncModeProvider as __experimentalAsyncModeProvider, -} from './components/async-mode-provider'; +export { AsyncModeProvider } from './components/async-mode-provider'; export { createRegistry } from './registry'; export { createRegistrySelector, createRegistryControl } from './factory'; diff --git a/packages/data/src/namespace-store/index.js b/packages/data/src/namespace-store/index.js index b3bd639fcddfa3..bc41587569bbba 100644 --- a/packages/data/src/namespace-store/index.js +++ b/packages/data/src/namespace-store/index.js @@ -24,7 +24,7 @@ import * as metadataSelectors from './metadata/selectors'; import * as metadataActions from './metadata/actions'; /** - * @typedef {import('../registry').WPDataRegistry} WPDataRegistry + * @typedef {WPDataRegistry} WPDataRegistry */ /** diff --git a/packages/data/src/plugins/persistence/index.js b/packages/data/src/plugins/persistence/index.js index a0c910f8a73bca..871a7e56133594 100644 --- a/packages/data/src/plugins/persistence/index.js +++ b/packages/data/src/plugins/persistence/index.js @@ -138,7 +138,7 @@ const persistencePlugin = function( registry, pluginOptions ) { // to leverage its behavior of returning the same object when none // of the property values changes. This allows a strict reference // equality to bypass a persistence set on an unchanging state. - const reducers = keys.reduce( ( result, key ) => Object.assign( result, { + const reducers = keys.reduce( ( accumulator, key ) => Object.assign( accumulator, { [ key ]: ( state, action ) => action.nextState[ key ], } ), {} ); diff --git a/packages/date/README.md b/packages/date/README.md index c4bad6ef1636e1..96cbbba1b7239d 100644 --- a/packages/date/README.md +++ b/packages/date/README.md @@ -23,7 +23,7 @@ Formats a date (like `date()` in PHP), in the site's timezone. _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|moment|null)`: Date object or string, parsable by moment.js. +- _dateValue_ `(Date|string|moment.Moment|null)`: Date object or string, parsable by moment.js. _Returns_ @@ -36,7 +36,7 @@ Formats a date (like `date_i18n()` in PHP). _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|moment|null)`: Date object or string, parsable by moment.js. +- _dateValue_ `(Date|string|moment.Moment|null)`: Date object or string, parsable by moment.js. - _gmt_ `boolean`: True for GMT/UTC, false for site's timezone. _Returns_ @@ -50,7 +50,7 @@ Formats a date. Does not alter the date's timezone. _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|moment|null)`: Date object or string, parsable by moment.js. +- _dateValue_ `(Date|string|moment.Moment|null)`: Date object or string, parsable by moment.js. _Returns_ @@ -75,7 +75,7 @@ Formats a date (like `date()` in PHP), in the UTC timezone. _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|moment|null)`: Date object or string, parsable by moment.js. +- _dateValue_ `(Date|string|moment.Moment|null)`: Date object or string, parsable by moment.js. _Returns_ diff --git a/packages/date/src/index.js b/packages/date/src/index.js index 39b4425ea2da22..4537ec47284749 100644 --- a/packages/date/src/index.js +++ b/packages/date/src/index.js @@ -145,7 +145,7 @@ const formatMap = { /** * Gets the ordinal suffix. * - * @param {moment} momentDate Moment instance. + * @param {moment.Moment} momentDate Moment instance. * * @return {string} Formatted date. */ @@ -160,7 +160,7 @@ const formatMap = { /** * Gets the day of the year (zero-indexed). * - * @param {moment} momentDate Moment instance. + * @param {moment.Moment} momentDate Moment instance. * * @return {string} Formatted date. */ @@ -180,7 +180,7 @@ const formatMap = { /** * Gets the days in the month. * - * @param {moment} momentDate Moment instance. + * @param {moment.Moment} momentDate Moment instance. * * @return {string} Formatted date. */ @@ -192,7 +192,7 @@ const formatMap = { /** * Gets whether the current year is a leap year. * - * @param {moment} momentDate Moment instance. + * @param {moment.Moment} momentDate Moment instance. * * @return {string} Formatted date. */ @@ -209,7 +209,7 @@ const formatMap = { /** * Gets the current time in Swatch Internet Time (.beats). * - * @param {moment} momentDate Moment instance. + * @param {moment.Moment} momentDate Moment instance. * * @return {string} Formatted date. */ @@ -240,7 +240,7 @@ const formatMap = { /** * Gets whether the timezone is in DST currently. * - * @param {moment} momentDate Moment instance. + * @param {moment.Moment} momentDate Moment instance. * * @return {string} Formatted date. */ @@ -253,7 +253,7 @@ const formatMap = { /** * Gets the timezone offset in seconds. * - * @param {moment} momentDate Moment instance. + * @param {moment.Moment} momentDate Moment instance. * * @return {string} Formatted date. */ @@ -273,10 +273,10 @@ const formatMap = { /** * Formats a date. Does not alter the date's timezone. * - * @param {string} dateFormat PHP-style formatting string. - * See php.net/date. - * @param {(Date|string|moment|null)} dateValue Date object or string, - * parsable by moment.js. + * @param {string} dateFormat PHP-style formatting string. + * See php.net/date. + * @param {(Date|string|moment.Moment|null)} dateValue Date object or string, + * parsable by moment.js. * * @return {string} Formatted date. */ @@ -314,10 +314,10 @@ export function format( dateFormat, dateValue = new Date() ) { /** * Formats a date (like `date()` in PHP), in the site's timezone. * - * @param {string} dateFormat PHP-style formatting string. - * See php.net/date. - * @param {(Date|string|moment|null)} dateValue Date object or string, - * parsable by moment.js. + * @param {string} dateFormat PHP-style formatting string. + * See php.net/date. + * @param {(Date|string|moment.Moment|null)} dateValue Date object or string, + * parsable by moment.js. * * @return {string} Formatted date. */ @@ -330,10 +330,10 @@ export function date( dateFormat, dateValue = new Date() ) { /** * Formats a date (like `date()` in PHP), in the UTC timezone. * - * @param {string} dateFormat PHP-style formatting string. - * See php.net/date. - * @param {(Date|string|moment|null)} dateValue Date object or string, - * parsable by moment.js. + * @param {string} dateFormat PHP-style formatting string. + * See php.net/date. + * @param {(Date|string|moment.Moment|null)} dateValue Date object or string, + * parsable by moment.js. * * @return {string} Formatted date. */ @@ -345,12 +345,12 @@ export function gmdate( dateFormat, dateValue = new Date() ) { /** * Formats a date (like `date_i18n()` in PHP). * - * @param {string} dateFormat PHP-style formatting string. - * See php.net/date. - * @param {(Date|string|moment|null)} dateValue Date object or string, - * parsable by moment.js. - * @param {boolean} gmt True for GMT/UTC, false for - * site's timezone. + * @param {string} dateFormat PHP-style formatting string. + * See php.net/date. + * @param {(Date|string|moment.Moment|null)} dateValue Date object or string, + * parsable by moment.js. + * @param {boolean} gmt True for GMT/UTC, false for + * site's timezone. * * @return {string} Formatted date. */ diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 654149a2f32208..7a0494fd4d54bb 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "2.6.0", + "version": "2.6.1", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 33e1f9eca8283d..ff4900eca80a02 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "2.5.0", + "version": "2.5.1", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/dom/package.json b/packages/dom/package.json index e5f1b9297eec16..b335970b2113bc 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "2.5.0", + "version": "2.5.2", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md index df00ecbe7c0ce2..6d7c17d4e0c7e6 100644 --- a/packages/e2e-test-utils/CHANGELOG.md +++ b/packages/e2e-test-utils/CHANGELOG.md @@ -1,3 +1,9 @@ +## Master + +### Breaking Changes +- The util function `enableExperimentalFeatures` was removed. It is now available for internal usage in the `e2e-tests` package. + + ## 2.0.0 (2019-05-21) ### Requirements diff --git a/packages/e2e-test-utils/README.md b/packages/e2e-test-utils/README.md index d8b3263c2e6e99..e01082057a82a8 100644 --- a/packages/e2e-test-utils/README.md +++ b/packages/e2e-test-utils/README.md @@ -191,7 +191,7 @@ _Parameters_ _Returns_ -- `?ElementHandle`: Object that represents an in-page DOM element. +- `?puppeteer.ElementHandle`: Object that represents an in-page DOM element. <a name="findSidebarPanelWithTitle" href="#findSidebarPanelWithTitle">#</a> **findSidebarPanelWithTitle** @@ -203,7 +203,7 @@ _Parameters_ _Returns_ -- `?ElementHandle`: Object that represents an in-page DOM element. +- `?puppeteer.ElementHandle`: Object that represents an in-page DOM element. <a name="getAllBlockInserterItemTitles" href="#getAllBlockInserterItemTitles">#</a> **getAllBlockInserterItemTitles** @@ -529,7 +529,7 @@ without the new dimensions being applied. _Parameters_ - _width_ `number`: Width of the window. -- _height_ `height`: Height of the window. +- _height_ `number`: Height of the window. <!-- END TOKEN(Autogenerated API docs) --> diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index 7ddd91f1b73974..6365c171aa416e 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "2.4.0", + "version": "2.4.3", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-test-utils/src/find-sidebar-panel-toggle-button-with-title.js b/packages/e2e-test-utils/src/find-sidebar-panel-toggle-button-with-title.js index a4167fe6e2e6cd..55fbf4c91c10f2 100644 --- a/packages/e2e-test-utils/src/find-sidebar-panel-toggle-button-with-title.js +++ b/packages/e2e-test-utils/src/find-sidebar-panel-toggle-button-with-title.js @@ -8,7 +8,7 @@ import { first } from 'lodash'; * * @param {string} panelTitle The name of sidebar panel. * - * @return {?ElementHandle} Object that represents an in-page DOM element. + * @return {?puppeteer.ElementHandle} Object that represents an in-page DOM element. */ export async function findSidebarPanelToggleButtonWithTitle( panelTitle ) { return first( await page.$x( `//div[contains(@class,"edit-post-sidebar")]//button[@class="components-button components-panel__body-toggle"][contains(text(),"${ panelTitle }")]` ) ); diff --git a/packages/e2e-test-utils/src/find-sidebar-panel-with-title.js b/packages/e2e-test-utils/src/find-sidebar-panel-with-title.js index ff60dca5f00782..2f1436c5b8de23 100644 --- a/packages/e2e-test-utils/src/find-sidebar-panel-with-title.js +++ b/packages/e2e-test-utils/src/find-sidebar-panel-with-title.js @@ -8,7 +8,7 @@ import { first } from 'lodash'; * * @param {string} panelTitle The name of sidebar panel. * - * @return {?ElementHandle} Object that represents an in-page DOM element. + * @return {?puppeteer.ElementHandle} Object that represents an in-page DOM element. */ export async function findSidebarPanelWithTitle( panelTitle ) { const classSelect = ( className ) => `[contains(concat(" ", @class, " "), " ${ className } ")]`; diff --git a/packages/e2e-test-utils/src/index.js b/packages/e2e-test-utils/src/index.js index 8fcf2b400c5853..40feec01014580 100644 --- a/packages/e2e-test-utils/src/index.js +++ b/packages/e2e-test-utils/src/index.js @@ -11,7 +11,6 @@ export { createURL } from './create-url'; export { deactivatePlugin } from './deactivate-plugin'; export { disablePrePublishChecks } from './disable-pre-publish-checks'; export { dragAndResize } from './drag-and-resize'; -export { enableExperimentalFeatures } from './enable-experimental-features'; export { enablePageDialogAccept } from './enable-page-dialog-accept'; export { enablePrePublishChecks } from './enable-pre-publish-checks'; export { ensureSidebarOpened } from './ensure-sidebar-opened'; diff --git a/packages/e2e-test-utils/src/wait-for-window-dimensions.js b/packages/e2e-test-utils/src/wait-for-window-dimensions.js index 9b505cf3b12807..924a5c939b3da0 100644 --- a/packages/e2e-test-utils/src/wait-for-window-dimensions.js +++ b/packages/e2e-test-utils/src/wait-for-window-dimensions.js @@ -5,7 +5,7 @@ * https://github.com/GoogleChrome/puppeteer/issues/1751 * * @param {number} width Width of the window. - * @param {height} height Height of the window. + * @param {number} height Height of the window. */ export async function waitForWindowDimensions( width, height ) { await page diff --git a/packages/e2e-tests/config/performance-reporter.js b/packages/e2e-tests/config/performance-reporter.js index 681bb60f41618e..f7bb05666054d5 100644 --- a/packages/e2e-tests/config/performance-reporter.js +++ b/packages/e2e-tests/config/performance-reporter.js @@ -6,7 +6,7 @@ function average( array ) { class PerformanceReporter { onRunComplete() { - const path = __dirname + '/../specs/results.json'; + const path = __dirname + '/../specs/performance/results.json'; if ( ! existsSync( path ) ) { return; diff --git a/packages/e2e-test-utils/src/enable-experimental-features.js b/packages/e2e-tests/experimental-features.js similarity index 60% rename from packages/e2e-test-utils/src/enable-experimental-features.js rename to packages/e2e-tests/experimental-features.js index be74f20f82fd92..4b06a761c865a0 100644 --- a/packages/e2e-test-utils/src/enable-experimental-features.js +++ b/packages/e2e-tests/experimental-features.js @@ -2,18 +2,9 @@ * WordPress dependencies */ import { addQueryArgs } from '@wordpress/url'; +import { visitAdminPage } from '@wordpress/e2e-test-utils'; -/** - * Internal dependencies - */ -import { visitAdminPage } from './visit-admin-page'; - -/** - * Enables experimental features from the plugin settings section. - * - * @param {Array} features Array of {string} selectors of settings to enable. Assumes they can be enabled with one click. - */ -export async function enableExperimentalFeatures( features ) { +async function setExperimentalFeaturesState( features, enable ) { const query = addQueryArgs( '', { page: 'gutenberg-experiments', } ); @@ -23,7 +14,7 @@ export async function enableExperimentalFeatures( features ) { await page.waitForSelector( feature ); const checkedSelector = `${ feature }[checked=checked]`; const isChecked = !! ( await page.$( checkedSelector ) ); - if ( ! isChecked ) { + if ( ( ! isChecked && enable ) || ( isChecked && ! enable ) ) { await page.click( feature ); } } ) ); @@ -32,3 +23,21 @@ export async function enableExperimentalFeatures( features ) { page.click( '#submit' ), ] ); } + +/** + * Enables experimental features from the plugin settings section. + * + * @param {Array} features Array of {string} selectors of settings to enable. Assumes they can be enabled with one click. + */ +export async function enableExperimentalFeatures( features ) { + await setExperimentalFeaturesState( features, true ); +} + +/** + * Disables experimental features from the plugin settings section. + * + * @param {Array} features Array of {string} selectors of settings to disable. Assumes they can be disabled with one click. + */ +export async function disableExperimentalFeatures( features ) { + await setExperimentalFeaturesState( features, false ); +} diff --git a/packages/e2e-tests/fixtures/block-transforms.js b/packages/e2e-tests/fixtures/block-transforms.js index ff297c84a5d3da..454bf2aeb2bf22 100644 --- a/packages/e2e-tests/fixtures/block-transforms.js +++ b/packages/e2e-tests/fixtures/block-transforms.js @@ -118,6 +118,28 @@ export const EXPECTED_TRANSFORMS = { ], originalBlock: 'Cover', }, + core__cover__gradient: { + availableTransforms: [ + 'Group', + 'Image', + 'Video', + ], + originalBlock: 'Cover', + }, + 'core__cover__gradient-image': { + availableTransforms: [ + 'Group', + 'Image', + ], + originalBlock: 'Cover', + }, + 'core__cover__gradient-video': { + availableTransforms: [ + 'Group', + 'Video', + ], + originalBlock: 'Cover', + }, core__cover__video: { availableTransforms: [ 'Group', @@ -425,6 +447,12 @@ export const EXPECTED_TRANSFORMS = { 'Group', ], }, + 'core__site-title': { + availableTransforms: [ + 'Group', + ], + originalBlock: 'Site Title', + }, 'core__social-link-amazon': { availableTransforms: [ 'Group', diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.html b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.html new file mode 100644 index 00000000000000..6353eac851e882 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.html @@ -0,0 +1,10 @@ +<!-- wp:cover {"url":"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=","dimRatio":30,"customGradient":"linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)"} --> +<div class="wp-block-cover has-background-dim-30 has-background-dim has-background-gradient" style="background-image:url(data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=)"> + <span aria-hidden="true" class="wp-block-cover__gradient-background" style="background:linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)"></span> + <div class="wp-block-cover__inner-container"> + <!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} --> + <p class="has-text-align-center has-large-font-size"> Cover! </p> + <!-- /wp:paragraph --> + </div> +</div> +<!-- /wp:cover --> diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.json b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.json new file mode 100644 index 00000000000000..7752fa1da96f25 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.json @@ -0,0 +1,31 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/cover", + "isValid": true, + "attributes": { + "url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=", + "hasParallax": false, + "dimRatio": 30, + "backgroundType": "image", + "customGradient": "linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)" + }, + "innerBlocks": [ + { + "clientId": "_clientId_0", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "align": "center", + "content": " Cover! ", + "dropCap": false, + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "originalContent": "<p class=\"has-text-align-center has-large-font-size\"> Cover! </p>" + } + ], + "originalContent": "<div class=\"wp-block-cover has-background-dim-30 has-background-dim has-background-gradient\" style=\"background-image:url(data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=)\">\n\t<span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background\" style=\"background:linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)\"></span>\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t\n\t</div>\n</div>" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.parsed.json b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.parsed.json new file mode 100644 index 00000000000000..07ab0aa6299bad --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.parsed.json @@ -0,0 +1,40 @@ +[ + { + "blockName": "core/cover", + "attrs": { + "url": "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=", + "dimRatio": 30, + "customGradient": "linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)" + }, + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": { + "align": "center", + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "innerHTML": "\n\t\t<p class=\"has-text-align-center has-large-font-size\"> Cover! </p>\n\t\t", + "innerContent": [ + "\n\t\t<p class=\"has-text-align-center has-large-font-size\"> Cover! </p>\n\t\t" + ] + } + ], + "innerHTML": "\n<div class=\"wp-block-cover has-background-dim-30 has-background-dim has-background-gradient\" style=\"background-image:url(data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=)\">\n\t<span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background\" style=\"background:linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)\"></span>\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t\n\t</div>\n</div>\n", + "innerContent": [ + "\n<div class=\"wp-block-cover has-background-dim-30 has-background-dim has-background-gradient\" style=\"background-image:url(data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=)\">\n\t<span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background\" style=\"background:linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)\"></span>\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t", + null, + "\n\t</div>\n</div>\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.serialized.html b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.serialized.html new file mode 100644 index 00000000000000..0fda894680f92b --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-image.serialized.html @@ -0,0 +1,5 @@ +<!-- wp:cover {"url":"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=","dimRatio":30,"customGradient":"linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)"} --> +<div class="wp-block-cover has-background-dim-30 has-background-dim has-background-gradient" style="background-image:url(data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=)"><span aria-hidden="true" class="wp-block-cover__gradient-background" style="background:linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)"></span><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} --> +<p class="has-text-align-center has-large-font-size"> Cover! </p> +<!-- /wp:paragraph --></div></div> +<!-- /wp:cover --> diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.html b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.html new file mode 100644 index 00000000000000..8da6141aa041ab --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.html @@ -0,0 +1,11 @@ +<!-- wp:cover {"url":"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=","dimRatio":70,"backgroundType":"video","customGradient":"linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)"} --> +<div class="wp-block-cover has-background-dim-70 has-background-dim has-background-gradient"> + <span aria-hidden="true" class="wp-block-cover__gradient-background" style="background:linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)"></span> + <video class="wp-block-cover__video-background" autoplay muted loop src="data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE="></video> + <div class="wp-block-cover__inner-container"> + <!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} --> + <p class="has-text-align-center has-large-font-size">Cover!</p> + <!-- /wp:paragraph --> + </div> +</div> +<!-- /wp:cover --> diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.json b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.json new file mode 100644 index 00000000000000..84cb314d0fcca6 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.json @@ -0,0 +1,31 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/cover", + "isValid": true, + "attributes": { + "url": "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=", + "hasParallax": false, + "dimRatio": 70, + "backgroundType": "video", + "customGradient": "linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)" + }, + "innerBlocks": [ + { + "clientId": "_clientId_0", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "align": "center", + "content": "Cover!", + "dropCap": false, + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "originalContent": "<p class=\"has-text-align-center has-large-font-size\">Cover!</p>" + } + ], + "originalContent": "<div class=\"wp-block-cover has-background-dim-70 has-background-dim has-background-gradient\">\n\t<span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background\" style=\"background:linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)\"></span>\n\t<video class=\"wp-block-cover__video-background\" autoplay muted loop src=\"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=\"></video>\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t\n\t</div>\n</div>" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.parsed.json b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.parsed.json new file mode 100644 index 00000000000000..3ad05dacbf1257 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.parsed.json @@ -0,0 +1,41 @@ +[ + { + "blockName": "core/cover", + "attrs": { + "url": "data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=", + "dimRatio": 70, + "backgroundType": "video", + "customGradient": "linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)" + }, + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": { + "align": "center", + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "innerHTML": "\n\t\t<p class=\"has-text-align-center has-large-font-size\">Cover!</p>\n\t\t", + "innerContent": [ + "\n\t\t<p class=\"has-text-align-center has-large-font-size\">Cover!</p>\n\t\t" + ] + } + ], + "innerHTML": "\n<div class=\"wp-block-cover has-background-dim-70 has-background-dim has-background-gradient\">\n\t<span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background\" style=\"background:linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)\"></span>\n\t<video class=\"wp-block-cover__video-background\" autoplay muted loop src=\"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=\"></video>\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t\n\t</div>\n</div>\n", + "innerContent": [ + "\n<div class=\"wp-block-cover has-background-dim-70 has-background-dim has-background-gradient\">\n\t<span aria-hidden=\"true\" class=\"wp-block-cover__gradient-background\" style=\"background:linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)\"></span>\n\t<video class=\"wp-block-cover__video-background\" autoplay muted loop src=\"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=\"></video>\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t", + null, + "\n\t</div>\n</div>\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.serialized.html b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.serialized.html new file mode 100644 index 00000000000000..2bc567b4bf2e03 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient-video.serialized.html @@ -0,0 +1,5 @@ +<!-- wp:cover {"url":"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=","dimRatio":70,"backgroundType":"video","customGradient":"linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)"} --> +<div class="wp-block-cover has-background-dim-70 has-background-dim has-background-gradient"><span aria-hidden="true" class="wp-block-cover__gradient-background" style="background:linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)"></span><video class="wp-block-cover__video-background" autoplay muted loop src="data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE="></video><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} --> +<p class="has-text-align-center has-large-font-size">Cover!</p> +<!-- /wp:paragraph --></div></div> +<!-- /wp:cover --> diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient.html b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.html new file mode 100644 index 00000000000000..e3e7b9af44777a --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.html @@ -0,0 +1,9 @@ +<!-- wp:cover {"customGradient":"linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)"} --> +<div class="wp-block-cover has-background-dim has-background-gradient" style="background:linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)"> + <div class="wp-block-cover__inner-container"> + <!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} --> + <p class="has-text-align-center has-large-font-size">Cover!</p> + <!-- /wp:paragraph --> + </div> +</div> +<!-- /wp:cover --> diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient.json b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.json new file mode 100644 index 00000000000000..c280fe63d55ab1 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.json @@ -0,0 +1,30 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/cover", + "isValid": true, + "attributes": { + "hasParallax": false, + "dimRatio": 50, + "backgroundType": "image", + "customGradient": "linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)" + }, + "innerBlocks": [ + { + "clientId": "_clientId_0", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "align": "center", + "content": "Cover!", + "dropCap": false, + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "originalContent": "<p class=\"has-text-align-center has-large-font-size\">Cover!</p>" + } + ], + "originalContent": "<div class=\"wp-block-cover has-background-dim has-background-gradient\" style=\"background:linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)\">\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t\n\t</div>\n</div>" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient.parsed.json b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.parsed.json new file mode 100644 index 00000000000000..423586ba2681e6 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.parsed.json @@ -0,0 +1,38 @@ +[ + { + "blockName": "core/cover", + "attrs": { + "customGradient": "linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)" + }, + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": { + "align": "center", + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "innerHTML": "\n\t\t<p class=\"has-text-align-center has-large-font-size\">Cover!</p>\n\t\t", + "innerContent": [ + "\n\t\t<p class=\"has-text-align-center has-large-font-size\">Cover!</p>\n\t\t" + ] + } + ], + "innerHTML": "\n<div class=\"wp-block-cover has-background-dim has-background-gradient\" style=\"background:linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)\">\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t\n\t</div>\n</div>\n", + "innerContent": [ + "\n<div class=\"wp-block-cover has-background-dim has-background-gradient\" style=\"background:linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)\">\n\t<div class=\"wp-block-cover__inner-container\">\n\t\t", + null, + "\n\t</div>\n</div>\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__cover__gradient.serialized.html b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.serialized.html new file mode 100644 index 00000000000000..92615e65131f9b --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__cover__gradient.serialized.html @@ -0,0 +1,5 @@ +<!-- wp:cover {"customGradient":"linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)"} --> +<div class="wp-block-cover has-background-dim has-background-gradient" style="background:linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)"><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} --> +<p class="has-text-align-center has-large-font-size">Cover!</p> +<!-- /wp:paragraph --></div></div> +<!-- /wp:cover --> diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.html b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.html index c88644e748ebf8..0ad94205cbce05 100644 --- a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.html +++ b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.html @@ -1,2 +1,2 @@ -<!-- wp:navigation-menu-item {"label":"WordPress","destination":"https://wordpress.org/"} --> +<!-- wp:navigation-menu-item {"label":"WordPress","url":"https://wordpress.org/"} --> <!-- /wp:navigation-menu-item --> diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.json b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.json index eb71bb8b929c4b..4e5ae943cb9b4a 100644 --- a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.json +++ b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.json @@ -5,9 +5,9 @@ "isValid": true, "attributes": { "label": "WordPress", - "destination": "https://wordpress.org/", "nofollow": false, - "opensInNewTab": false + "opensInNewTab": false, + "url": "https://wordpress.org/" }, "innerBlocks": [], "originalContent": "" diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.parsed.json b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.parsed.json index 8fa4f5340bb83d..2b03a8420038b8 100644 --- a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.parsed.json +++ b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.parsed.json @@ -3,7 +3,7 @@ "blockName": "core/navigation-menu-item", "attrs": { "label": "WordPress", - "destination": "https://wordpress.org/" + "url": "https://wordpress.org/" }, "innerBlocks": [], "innerHTML": "\n", diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.serialized.html b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.serialized.html index b5176129ef2460..ecf1f0ce7ad654 100644 --- a/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.serialized.html +++ b/packages/e2e-tests/fixtures/blocks/core__navigation-menu-item.serialized.html @@ -1 +1 @@ -<!-- wp:navigation-menu-item {"label":"WordPress","destination":"https://wordpress.org/"} /--> +<!-- wp:navigation-menu-item {"label":"WordPress","url":"https://wordpress.org/"} /--> diff --git a/packages/e2e-tests/fixtures/blocks/core__site-title.html b/packages/e2e-tests/fixtures/blocks/core__site-title.html new file mode 100644 index 00000000000000..3c42ebc0d2feb7 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__site-title.html @@ -0,0 +1 @@ +<!-- wp:site-title /--> diff --git a/packages/e2e-tests/fixtures/blocks/core__site-title.json b/packages/e2e-tests/fixtures/blocks/core__site-title.json new file mode 100644 index 00000000000000..6070316cf41796 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__site-title.json @@ -0,0 +1,10 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/site-title", + "isValid": true, + "attributes": {}, + "innerBlocks": [], + "originalContent": "" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__site-title.parsed.json b/packages/e2e-tests/fixtures/blocks/core__site-title.parsed.json new file mode 100644 index 00000000000000..bc570f8255a124 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__site-title.parsed.json @@ -0,0 +1,18 @@ +[ + { + "blockName": "core/site-title", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__site-title.serialized.html b/packages/e2e-tests/fixtures/blocks/core__site-title.serialized.html new file mode 100644 index 00000000000000..3c42ebc0d2feb7 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__site-title.serialized.html @@ -0,0 +1 @@ +<!-- wp:site-title /--> diff --git a/packages/e2e-tests/jest.config.js b/packages/e2e-tests/jest.config.js index 2720488c7b36de..22bccf74e3d1a1 100644 --- a/packages/e2e-tests/jest.config.js +++ b/packages/e2e-tests/jest.config.js @@ -12,6 +12,6 @@ module.exports = { testPathIgnorePatterns: [ '/node_modules/', '/wordpress/', - 'e2e-tests/specs/performance.test.js', + 'e2e-tests/specs/performance/', ], }; diff --git a/packages/e2e-tests/jest.performance.config.js b/packages/e2e-tests/jest.performance.config.js index f8fe4cf8ff1ff6..cc011820f08871 100644 --- a/packages/e2e-tests/jest.performance.config.js +++ b/packages/e2e-tests/jest.performance.config.js @@ -1,7 +1,7 @@ module.exports = { ...require( '@wordpress/scripts/config/jest-e2e.config' ), testMatch: [ - '**/performance.test.js', + '**/performance/*.test.js', ], setupFiles: [ '<rootDir>/config/gutenberg-phase.js', diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 1fe2ee6a9464ba..b3a75cf9f73145 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "1.7.0", + "version": "1.7.3", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -26,6 +26,7 @@ "@wordpress/jest-console": "file:../jest-console", "@wordpress/jest-puppeteer-axe": "file:../jest-puppeteer-axe", "@wordpress/scripts": "file:../scripts", + "@wordpress/url": "file:../url", "expect-puppeteer": "^4.3.0", "lodash": "^4.17.15", "uuid": "^3.3.2" diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/button.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/button.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/button.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/button.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/classic.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/classic.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/classic.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/classic.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/code.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/code.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/code.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/code.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/group.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/group.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/group.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/group.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/heading.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/heading.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/heading.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/heading.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/html.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/html.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/html.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/html.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/list.test.js.snap similarity index 98% rename from packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/list.test.js.snap index 001c5b93e0edd3..b1e178aa10b710 100644 --- a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap +++ b/packages/e2e-tests/specs/editor/blocks/__snapshots__/list.test.js.snap @@ -80,6 +80,12 @@ exports[`List can undo asterisk transform 1`] = ` <!-- /wp:paragraph -->" `; +exports[`List first empty list item is graciously removed 1`] = ` +"<!-- wp:list --> +<ul><li>2</li></ul> +<!-- /wp:list -->" +`; + exports[`List should be immeadiately saved on indentation 1`] = ` "<!-- wp:list --> <ul><li>one<ul><li></li></ul></li></ul> diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/preformatted.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/preformatted.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/preformatted.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/preformatted.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/quote.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/quote.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/quote.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/quote.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/separator.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/separator.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/separator.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/separator.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/spacer.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/spacer.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/spacer.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/spacer.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/table.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/table.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/blocks/__snapshots__/table.test.js.snap rename to packages/e2e-tests/specs/editor/blocks/__snapshots__/table.test.js.snap diff --git a/packages/e2e-tests/specs/blocks/button.test.js b/packages/e2e-tests/specs/editor/blocks/button.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/button.test.js rename to packages/e2e-tests/specs/editor/blocks/button.test.js diff --git a/packages/e2e-tests/specs/blocks/classic.test.js b/packages/e2e-tests/specs/editor/blocks/classic.test.js similarity index 95% rename from packages/e2e-tests/specs/blocks/classic.test.js rename to packages/e2e-tests/specs/editor/blocks/classic.test.js index 29d1605f0874b5..29170335d900f1 100644 --- a/packages/e2e-tests/specs/blocks/classic.test.js +++ b/packages/e2e-tests/specs/editor/blocks/classic.test.js @@ -49,7 +49,7 @@ describe( 'Classic', () => { // Wait for media modal to appear and upload image. await page.waitForSelector( '.media-modal input[type=file]' ); const inputElement = await page.$( '.media-modal input[type=file]' ); - const testImagePath = path.join( __dirname, '..', '..', 'assets', '10x10_e2e_test_image_z9T8jK.png' ); + const testImagePath = path.join( __dirname, '..', '..', '..', 'assets', '10x10_e2e_test_image_z9T8jK.png' ); const filename = uuid(); const tmpFileName = path.join( os.tmpdir(), filename + '.png' ); fs.copyFileSync( testImagePath, tmpFileName ); diff --git a/packages/e2e-tests/specs/blocks/code.test.js b/packages/e2e-tests/specs/editor/blocks/code.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/code.test.js rename to packages/e2e-tests/specs/editor/blocks/code.test.js diff --git a/packages/e2e-tests/specs/blocks/columns.test.js b/packages/e2e-tests/specs/editor/blocks/columns.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/columns.test.js rename to packages/e2e-tests/specs/editor/blocks/columns.test.js diff --git a/packages/e2e-tests/specs/blocks/group.test.js b/packages/e2e-tests/specs/editor/blocks/group.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/group.test.js rename to packages/e2e-tests/specs/editor/blocks/group.test.js diff --git a/packages/e2e-tests/specs/blocks/heading.test.js b/packages/e2e-tests/specs/editor/blocks/heading.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/heading.test.js rename to packages/e2e-tests/specs/editor/blocks/heading.test.js diff --git a/packages/e2e-tests/specs/blocks/html.test.js b/packages/e2e-tests/specs/editor/blocks/html.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/html.test.js rename to packages/e2e-tests/specs/editor/blocks/html.test.js diff --git a/packages/e2e-tests/specs/blocks/list.test.js b/packages/e2e-tests/specs/editor/blocks/list.test.js similarity index 97% rename from packages/e2e-tests/specs/blocks/list.test.js rename to packages/e2e-tests/specs/editor/blocks/list.test.js index 7fa889baa0cdd3..9e3086295413e9 100644 --- a/packages/e2e-tests/specs/blocks/list.test.js +++ b/packages/e2e-tests/specs/editor/blocks/list.test.js @@ -478,4 +478,16 @@ describe( 'List', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'first empty list item is graciously removed', async () => { + await clickBlockAppender(); + await page.keyboard.type( '* 1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } ); diff --git a/packages/e2e-tests/specs/blocks/preformatted.test.js b/packages/e2e-tests/specs/editor/blocks/preformatted.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/preformatted.test.js rename to packages/e2e-tests/specs/editor/blocks/preformatted.test.js diff --git a/packages/e2e-tests/specs/blocks/quote.test.js b/packages/e2e-tests/specs/editor/blocks/quote.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/quote.test.js rename to packages/e2e-tests/specs/editor/blocks/quote.test.js diff --git a/packages/e2e-tests/specs/blocks/separator.test.js b/packages/e2e-tests/specs/editor/blocks/separator.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/separator.test.js rename to packages/e2e-tests/specs/editor/blocks/separator.test.js diff --git a/packages/e2e-tests/specs/blocks/spacer.test.js b/packages/e2e-tests/specs/editor/blocks/spacer.test.js similarity index 100% rename from packages/e2e-tests/specs/blocks/spacer.test.js rename to packages/e2e-tests/specs/editor/blocks/spacer.test.js diff --git a/packages/e2e-tests/specs/blocks/table.test.js b/packages/e2e-tests/specs/editor/blocks/table.test.js similarity index 94% rename from packages/e2e-tests/specs/blocks/table.test.js rename to packages/e2e-tests/specs/editor/blocks/table.test.js index 15267da90cb185..790ba0cd1ad7d6 100644 --- a/packages/e2e-tests/specs/blocks/table.test.js +++ b/packages/e2e-tests/specs/editor/blocks/table.test.js @@ -70,7 +70,7 @@ describe( 'Table', () => { await clickButton( createButtonLabel ); // Click the first cell and add some text. - await page.click( '.wp-block-table__cell-content' ); + await page.click( 'td' ); await page.keyboard.type( 'This' ); // Tab to the next cell and add some text. @@ -114,13 +114,13 @@ describe( 'Table', () => { await headerSwitch[ 0 ].click(); await footerSwitch[ 0 ].click(); - await page.click( 'thead .wp-block-table__cell-content' ); + await page.click( 'thead th' ); await page.keyboard.type( 'header' ); - await page.click( 'tbody .wp-block-table__cell-content' ); + await page.click( 'tbody td' ); await page.keyboard.type( 'body' ); - await page.click( 'tfoot .wp-block-table__cell-content' ); + await page.click( 'tfoot td' ); await page.keyboard.type( 'footer' ); // Expect the table to have a header, body and footer with written content. @@ -146,7 +146,7 @@ describe( 'Table', () => { await headerSwitch[ 0 ].click(); await footerSwitch[ 0 ].click(); - await page.click( '.wp-block-table__cell-content' ); + await page.click( 'td' ); // Add a column. await clickBlockToolbarButton( 'Edit table' ); @@ -155,7 +155,7 @@ describe( 'Table', () => { // Expect the table to have 3 columns across the header, body and footer. expect( await getEditedPostContent() ).toMatchSnapshot(); - await page.click( '.wp-block-table__cell-content' ); + await page.click( 'td' ); // Delete a column. await clickBlockToolbarButton( 'Edit table' ); @@ -177,7 +177,7 @@ describe( 'Table', () => { await clickButton( createButtonLabel ); // Click the first cell and add some text. Don't align. - const cells = await page.$$( '.wp-block-table__cell-content' ); + const cells = await page.$$( 'td,th' ); await cells[ 0 ].click(); await page.keyboard.type( 'None' ); @@ -212,7 +212,7 @@ describe( 'Table', () => { await fixedWidthSwitch.click(); // Add multiple new lines to the first cell to make it taller. - await page.click( '.wp-block-table__cell-content' ); + await page.click( 'td' ); await page.keyboard.type( '\n\n\n\n' ); // Get the bounding client rect for the second cell. diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/align-hook.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/align-hook.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/align-hook.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/align-hook.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/container-blocks.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/container-blocks.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/cpt-locking.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/cpt-locking.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/cpt-locking.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/cpt-locking.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/deprecated-node-matcher.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/deprecated-node-matcher.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/deprecated-node-matcher.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/deprecated-node-matcher.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/format-api.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/format-api.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/format-api.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/format-api.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/hooks-api.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/hooks-api.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/hooks-api.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/hooks-api.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/meta-attribute-block.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/meta-attribute-block.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/meta-attribute-block.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/meta-attribute-block.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/plugins-api.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/plugins-api.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/plugins-api.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/plugins-api.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/templates.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/templates.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/templates.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/templates.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/wp-editor-meta-box.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/wp-editor-meta-box.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/plugins/__snapshots__/wp-editor-meta-box.test.js.snap rename to packages/e2e-tests/specs/editor/plugins/__snapshots__/wp-editor-meta-box.test.js.snap diff --git a/packages/e2e-tests/specs/plugins/align-hook.test.js b/packages/e2e-tests/specs/editor/plugins/align-hook.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/align-hook.test.js rename to packages/e2e-tests/specs/editor/plugins/align-hook.test.js diff --git a/packages/e2e-tests/specs/plugins/allowed-blocks.test.js b/packages/e2e-tests/specs/editor/plugins/allowed-blocks.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/allowed-blocks.test.js rename to packages/e2e-tests/specs/editor/plugins/allowed-blocks.test.js diff --git a/packages/e2e-tests/specs/plugins/annotations.test.js b/packages/e2e-tests/specs/editor/plugins/annotations.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/annotations.test.js rename to packages/e2e-tests/specs/editor/plugins/annotations.test.js diff --git a/packages/e2e-tests/specs/plugins/block-icons.test.js b/packages/e2e-tests/specs/editor/plugins/block-icons.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/block-icons.test.js rename to packages/e2e-tests/specs/editor/plugins/block-icons.test.js diff --git a/packages/e2e-tests/specs/plugins/container-blocks.test.js b/packages/e2e-tests/specs/editor/plugins/container-blocks.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/container-blocks.test.js rename to packages/e2e-tests/specs/editor/plugins/container-blocks.test.js diff --git a/packages/e2e-tests/specs/plugins/cpt-locking.test.js b/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/cpt-locking.test.js rename to packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js diff --git a/packages/e2e-tests/specs/plugins/custom-taxonomies.test.js b/packages/e2e-tests/specs/editor/plugins/custom-taxonomies.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/custom-taxonomies.test.js rename to packages/e2e-tests/specs/editor/plugins/custom-taxonomies.test.js diff --git a/packages/e2e-tests/specs/plugins/deprecated-node-matcher.test.js b/packages/e2e-tests/specs/editor/plugins/deprecated-node-matcher.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/deprecated-node-matcher.test.js rename to packages/e2e-tests/specs/editor/plugins/deprecated-node-matcher.test.js diff --git a/packages/e2e-tests/specs/plugins/format-api.test.js b/packages/e2e-tests/specs/editor/plugins/format-api.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/format-api.test.js rename to packages/e2e-tests/specs/editor/plugins/format-api.test.js diff --git a/packages/e2e-tests/specs/plugins/hooks-api.test.js b/packages/e2e-tests/specs/editor/plugins/hooks-api.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/hooks-api.test.js rename to packages/e2e-tests/specs/editor/plugins/hooks-api.test.js diff --git a/packages/e2e-tests/specs/plugins/inner-blocks-allowed-blocks.test.js b/packages/e2e-tests/specs/editor/plugins/inner-blocks-allowed-blocks.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/inner-blocks-allowed-blocks.test.js rename to packages/e2e-tests/specs/editor/plugins/inner-blocks-allowed-blocks.test.js diff --git a/packages/e2e-tests/specs/plugins/innerblocks-locking-all-embed.js b/packages/e2e-tests/specs/editor/plugins/innerblocks-locking-all-embed.js similarity index 100% rename from packages/e2e-tests/specs/plugins/innerblocks-locking-all-embed.js rename to packages/e2e-tests/specs/editor/plugins/innerblocks-locking-all-embed.js diff --git a/packages/e2e-tests/specs/plugins/meta-attribute-block.test.js b/packages/e2e-tests/specs/editor/plugins/meta-attribute-block.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/meta-attribute-block.test.js rename to packages/e2e-tests/specs/editor/plugins/meta-attribute-block.test.js diff --git a/packages/e2e-tests/specs/plugins/meta-boxes.test.js b/packages/e2e-tests/specs/editor/plugins/meta-boxes.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/meta-boxes.test.js rename to packages/e2e-tests/specs/editor/plugins/meta-boxes.test.js diff --git a/packages/e2e-tests/specs/plugins/nonce.test.js b/packages/e2e-tests/specs/editor/plugins/nonce.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/nonce.test.js rename to packages/e2e-tests/specs/editor/plugins/nonce.test.js diff --git a/packages/e2e-tests/specs/plugins/plugins-api.test.js b/packages/e2e-tests/specs/editor/plugins/plugins-api.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/plugins-api.test.js rename to packages/e2e-tests/specs/editor/plugins/plugins-api.test.js diff --git a/packages/e2e-tests/specs/plugins/templates.test.js b/packages/e2e-tests/specs/editor/plugins/templates.test.js similarity index 100% rename from packages/e2e-tests/specs/plugins/templates.test.js rename to packages/e2e-tests/specs/editor/plugins/templates.test.js diff --git a/packages/e2e-tests/specs/plugins/wp-editor-meta-box.test.js b/packages/e2e-tests/specs/editor/plugins/wp-editor-meta-box.test.js similarity index 76% rename from packages/e2e-tests/specs/plugins/wp-editor-meta-box.test.js rename to packages/e2e-tests/specs/editor/plugins/wp-editor-meta-box.test.js index 74dd45374e946d..32286764092f17 100644 --- a/packages/e2e-tests/specs/plugins/wp-editor-meta-box.test.js +++ b/packages/e2e-tests/specs/editor/plugins/wp-editor-meta-box.test.js @@ -8,9 +8,7 @@ import { publishPost, } from '@wordpress/e2e-test-utils'; -// This test isn't reliable on Travis and fails from time to time. -// See: https://github.com/WordPress/gutenberg/pull/15211. -describe.skip( 'WP Editor Meta Boxes', () => { +describe( 'WP Editor Meta Boxes', () => { beforeAll( async () => { await activatePlugin( 'gutenberg-test-plugin-wp-editor-meta-box' ); await createNewPost(); @@ -25,7 +23,7 @@ describe.skip( 'WP Editor Meta Boxes', () => { await page.type( '.editor-post-title__input', 'Hello Meta' ); // Type something - await page.click( '#test_tinymce_id-html' ); + await expect( page ).toClick( '#test_tinymce_id-html' ); await page.type( '#test_tinymce_id', 'Typing in a metabox' ); await page.click( '#test_tinymce_id-tmce' ); @@ -33,7 +31,8 @@ describe.skip( 'WP Editor Meta Boxes', () => { await page.reload(); - await page.click( '#test_tinymce_id-html' ); + await expect( page ).toClick( '#test_tinymce_id-html' ); + await page.waitForSelector( '#test_tinymce_id' ); const content = await page.$eval( '#test_tinymce_id', ( textarea ) => textarea.value diff --git a/packages/e2e-tests/specs/__snapshots__/adding-blocks.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/adding-blocks.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/adding-blocks.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/adding-blocks.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/block-deletion.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/block-deletion.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/block-deletion.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/block-deletion.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/block-grouping.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/block-grouping.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/block-grouping.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/block-grouping.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/block-hierarchy-navigation.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/block-hierarchy-navigation.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/block-hierarchy-navigation.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/block-hierarchy-navigation.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/compatibility-classic-editor.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/compatibility-classic-editor.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/compatibility-classic-editor.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/compatibility-classic-editor.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/convert-block-type.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/convert-block-type.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/convert-block-type.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/convert-block-type.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/embedding.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/embedding.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/embedding.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/embedding.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/font-size-picker.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/font-size-picker.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/font-size-picker.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/font-size-picker.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/links.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/links.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/mentions.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/mentions.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/mentions.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/mentions.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/multi-block-selection.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/multi-block-selection.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/reusable-blocks.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/reusable-blocks.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/reusable-blocks.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/reusable-blocks.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/rich-text.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/rich-text.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/rtl.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/rtl.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/rtl.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/rtl.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/splitting-merging.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/splitting-merging.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/splitting-merging.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/splitting-merging.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/style-variation.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/style-variation.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/style-variation.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/style-variation.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/undo.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/undo.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/undo.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/undo.test.js.snap diff --git a/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/writing-flow.test.js.snap similarity index 100% rename from packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap rename to packages/e2e-tests/specs/editor/various/__snapshots__/writing-flow.test.js.snap diff --git a/packages/e2e-tests/specs/a11y.test.js b/packages/e2e-tests/specs/editor/various/a11y.test.js similarity index 100% rename from packages/e2e-tests/specs/a11y.test.js rename to packages/e2e-tests/specs/editor/various/a11y.test.js diff --git a/packages/e2e-tests/specs/adding-blocks.test.js b/packages/e2e-tests/specs/editor/various/adding-blocks.test.js similarity index 98% rename from packages/e2e-tests/specs/adding-blocks.test.js rename to packages/e2e-tests/specs/editor/various/adding-blocks.test.js index 56e5976513746d..ea79e31224e8e0 100644 --- a/packages/e2e-tests/specs/adding-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/adding-blocks.test.js @@ -17,7 +17,7 @@ describe( 'adding blocks', () => { /** * Given a Puppeteer ElementHandle, clicks below its bounding box. * - * @param {Puppeteer.ElementHandle} elementHandle Element handle. + * @param {puppeteer.ElementHandle} elementHandle Element handle. * * @return {Promise} Promise resolving when click occurs. */ diff --git a/packages/e2e-tests/specs/adding-inline-tokens.test.js b/packages/e2e-tests/specs/editor/various/adding-inline-tokens.test.js similarity index 93% rename from packages/e2e-tests/specs/adding-inline-tokens.test.js rename to packages/e2e-tests/specs/editor/various/adding-inline-tokens.test.js index f028a73c3df931..b11077e70fb3d3 100644 --- a/packages/e2e-tests/specs/adding-inline-tokens.test.js +++ b/packages/e2e-tests/specs/editor/various/adding-inline-tokens.test.js @@ -33,7 +33,7 @@ describe( 'adding inline tokens', () => { // Wait for media modal to appear and upload image. await page.waitForSelector( '.media-modal input[type=file]' ); const inputElement = await page.$( '.media-modal input[type=file]' ); - const testImagePath = path.join( __dirname, '..', 'assets', '10x10_e2e_test_image_z9T8jK.png' ); + const testImagePath = path.join( __dirname, '..', '..', '..', 'assets', '10x10_e2e_test_image_z9T8jK.png' ); const filename = uuid(); const tmpFileName = path.join( os.tmpdir(), filename + '.png' ); fs.copyFileSync( testImagePath, tmpFileName ); diff --git a/packages/e2e-tests/specs/autosave.test.js b/packages/e2e-tests/specs/editor/various/autosave.test.js similarity index 77% rename from packages/e2e-tests/specs/autosave.test.js rename to packages/e2e-tests/specs/editor/various/autosave.test.js index 9f97f0f1c3f291..3c966f15a0748d 100644 --- a/packages/e2e-tests/specs/autosave.test.js +++ b/packages/e2e-tests/specs/editor/various/autosave.test.js @@ -17,13 +17,22 @@ const AUTOSAVE_INTERVAL_SECONDS = 5; const AUTOSAVE_NOTICE_REMOTE = 'There is an autosave of this post that is more recent than the version below.'; const AUTOSAVE_NOTICE_LOCAL = 'The backup of this post in your browser is different from the version below.'; +// Save and wait for "Saved" to confirm save complete. Preserves focus in the +// editing area. async function saveDraftWithKeyboard() { - return pressKeyWithModifier( 'primary', 's' ); + await page.waitForSelector( '.editor-post-save-draft' ); + await Promise.all( [ + page.waitForSelector( '.editor-post-saved-state.is-saved' ), + pressKeyWithModifier( 'primary', 'S' ), + ] ); } async function sleep( durationInSeconds ) { - return new Promise( ( resolve ) => - setTimeout( resolve, durationInSeconds * 1000 ) ); + // Rule `no-restricted-syntax` recommends `waitForSelector` against + // `waitFor`, which isn't apt for the use case, when provided an integer, + // of waiting for a given amount of time. + // eslint-disable-next-line no-restricted-syntax + await page.waitFor( durationInSeconds * 1000 ); } async function clearSessionStorage() { @@ -153,11 +162,11 @@ describe( 'autosave', () => { } ); it( 'should clear local autosave after successful remote autosave', async () => { + // Edit, save draft, edit again await clickBlockAppender(); await page.keyboard.type( 'before save' ); - await saveDraft(); - - await page.keyboard.type( 'after save' ); + await saveDraftWithKeyboard(); + await page.keyboard.type( ' after save' ); // Trigger local autosave await page.evaluate( () => window.wp.data.dispatch( 'core/editor' ).__experimentalLocalAutosave() ); @@ -169,21 +178,52 @@ describe( 'autosave', () => { } ); it( 'shouldn\'t clear local autosave if remote autosave fails', async () => { + // Edit, save draft, edit again await clickBlockAppender(); await page.keyboard.type( 'before save' ); - await saveDraft(); + await saveDraftWithKeyboard(); + await page.keyboard.type( ' after save' ); - await page.keyboard.type( 'after save' ); + // Trigger local autosave await page.evaluate( () => window.wp.data.dispatch( 'core/editor' ).__experimentalLocalAutosave() ); expect( await page.evaluate( () => window.sessionStorage.length ) ).toBe( 1 ); + // Bring network down and attempt to autosave remotely toggleOfflineMode( true ); - - // Trigger remote autosave await page.evaluate( () => window.wp.data.dispatch( 'core/editor' ).autosave() ); expect( await page.evaluate( () => window.sessionStorage.length ) ).toBe( 1 ); + } ); - toggleOfflineMode( false ); + it( 'should clear local autosave after successful save', async () => { + // Edit, save draft, edit again + await clickBlockAppender(); + await page.keyboard.type( 'before save' ); + await saveDraftWithKeyboard(); + await page.keyboard.type( ' after save' ); + + // Trigger local autosave + await page.evaluate( () => window.wp.data.dispatch( 'core/editor' ).__experimentalLocalAutosave() ); + expect( await page.evaluate( () => window.sessionStorage.length ) ).toBe( 1 ); + + await saveDraftWithKeyboard(); + expect( await page.evaluate( () => window.sessionStorage.length ) ).toBe( 0 ); + } ); + + it( 'shouldn\'t clear local autosave if save fails', async () => { + // Edit, save draft, edit again + await clickBlockAppender(); + await page.keyboard.type( 'before save' ); + await saveDraftWithKeyboard(); + await page.keyboard.type( ' after save' ); + + // Trigger local autosave + await page.evaluate( () => window.wp.data.dispatch( 'core/editor' ).__experimentalLocalAutosave() ); + expect( await page.evaluate( () => window.sessionStorage.length ) ).toBe( 1 ); + + // Bring network down and attempt to save + toggleOfflineMode( true ); + saveDraftWithKeyboard(); + expect( await page.evaluate( () => window.sessionStorage.length ) ).toBe( 1 ); } ); it( 'shouldn\'t conflict with server-side autosave', async () => { @@ -221,7 +261,8 @@ describe( 'autosave', () => { expect( notice ).toContain( AUTOSAVE_NOTICE_REMOTE ); } ); - afterAll( async () => { + afterEach( async () => { + toggleOfflineMode( false ); await clearSessionStorage(); } ); } ); diff --git a/packages/e2e-tests/specs/block-deletion.test.js b/packages/e2e-tests/specs/editor/various/block-deletion.test.js similarity index 100% rename from packages/e2e-tests/specs/block-deletion.test.js rename to packages/e2e-tests/specs/editor/various/block-deletion.test.js diff --git a/packages/e2e-tests/specs/block-grouping.test.js b/packages/e2e-tests/specs/editor/various/block-grouping.test.js similarity index 100% rename from packages/e2e-tests/specs/block-grouping.test.js rename to packages/e2e-tests/specs/editor/various/block-grouping.test.js diff --git a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js b/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js similarity index 99% rename from packages/e2e-tests/specs/block-hierarchy-navigation.test.js rename to packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js index 2dc5dc1f6cc544..efba2e1d6afc5e 100644 --- a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js +++ b/packages/e2e-tests/specs/editor/various/block-hierarchy-navigation.test.js @@ -82,6 +82,7 @@ describe( 'Navigating the block hierarchy', () => { await pressKeyWithModifier( 'ctrl', '`' ); await pressKeyWithModifier( 'ctrl', '`' ); await pressKeyWithModifier( 'ctrl', '`' ); + await pressKeyWithModifier( 'ctrl', '`' ); await pressKeyTimes( 'Tab', 4 ); // Tweak the columns count by increasing it by one. diff --git a/packages/e2e-tests/specs/block-mover.test.js b/packages/e2e-tests/specs/editor/various/block-mover.test.js similarity index 100% rename from packages/e2e-tests/specs/block-mover.test.js rename to packages/e2e-tests/specs/editor/various/block-mover.test.js diff --git a/packages/e2e-tests/specs/block-switcher.test.js b/packages/e2e-tests/specs/editor/various/block-switcher.test.js similarity index 100% rename from packages/e2e-tests/specs/block-switcher.test.js rename to packages/e2e-tests/specs/editor/various/block-switcher.test.js diff --git a/packages/e2e-tests/specs/change-detection.test.js b/packages/e2e-tests/specs/editor/various/change-detection.test.js similarity index 100% rename from packages/e2e-tests/specs/change-detection.test.js rename to packages/e2e-tests/specs/editor/various/change-detection.test.js diff --git a/packages/e2e-tests/specs/compatibility-classic-editor.test.js b/packages/e2e-tests/specs/editor/various/compatibility-classic-editor.test.js similarity index 100% rename from packages/e2e-tests/specs/compatibility-classic-editor.test.js rename to packages/e2e-tests/specs/editor/various/compatibility-classic-editor.test.js diff --git a/packages/e2e-tests/specs/convert-block-type.test.js b/packages/e2e-tests/specs/editor/various/convert-block-type.test.js similarity index 100% rename from packages/e2e-tests/specs/convert-block-type.test.js rename to packages/e2e-tests/specs/editor/various/convert-block-type.test.js diff --git a/packages/e2e-tests/specs/datepicker.test.js b/packages/e2e-tests/specs/editor/various/datepicker.test.js similarity index 100% rename from packages/e2e-tests/specs/datepicker.test.js rename to packages/e2e-tests/specs/editor/various/datepicker.test.js diff --git a/packages/e2e-tests/specs/editor-modes.test.js b/packages/e2e-tests/specs/editor/various/editor-modes.test.js similarity index 100% rename from packages/e2e-tests/specs/editor-modes.test.js rename to packages/e2e-tests/specs/editor/various/editor-modes.test.js diff --git a/packages/e2e-tests/specs/embedding.test.js b/packages/e2e-tests/specs/editor/various/embedding.test.js similarity index 87% rename from packages/e2e-tests/specs/embedding.test.js rename to packages/e2e-tests/specs/editor/various/embedding.test.js index 878e540967a4f1..0a986702075bec 100644 --- a/packages/e2e-tests/specs/embedding.test.js +++ b/packages/e2e-tests/specs/editor/various/embedding.test.js @@ -271,33 +271,4 @@ describe( 'Embedding content', () => { // Check the block has become a WordPress block. await page.waitForSelector( '.wp-block-embed-wordpress' ); } ); - - it.skip( 'should transform from video to embed block when YouTube URL is pasted', async () => { - await clickBlockAppender(); - await insertBlock( 'Video' ); - await page.click( '.editor-media-placeholder__url-input-container button' ); - await page.keyboard.type( 'https://www.youtube.com/watch?v=lXMskKTw3Bc' ); - await page.keyboard.press( 'Enter' ); - await page.waitForSelector( '.wp-block-embed-youtube' ); - } ); - - it.skip( 'should transform from image to embed block when Instagram URL is pasted', async () => { - await clickBlockAppender(); - await page.keyboard.type( '/image' ); - await page.keyboard.press( 'Enter' ); - await page.click( '.editor-media-placeholder__url-input-container button' ); - await page.keyboard.type( 'https://www.instagram.com/p/Bvl97o2AK6x/' ); - await page.keyboard.press( 'Enter' ); - await page.waitForSelector( '.wp-block-embed-instagram' ); - } ); - - it.skip( 'should transform from audio to embed block when Soundcloud URL is pasted', async () => { - await clickBlockAppender(); - await page.keyboard.type( '/audio' ); - await page.keyboard.press( 'Enter' ); - await page.click( '.editor-media-placeholder__url-input-container button' ); - await page.keyboard.type( 'https://soundcloud.com/a-boogie-wit-da-hoodie/swervin' ); - await page.keyboard.press( 'Enter' ); - await page.waitForSelector( '.wp-block-embed-soundcloud' ); - } ); } ); diff --git a/packages/e2e-tests/specs/font-size-picker.test.js b/packages/e2e-tests/specs/editor/various/font-size-picker.test.js similarity index 97% rename from packages/e2e-tests/specs/font-size-picker.test.js rename to packages/e2e-tests/specs/editor/various/font-size-picker.test.js index a80a81b46442d3..0fb98caa4f5d29 100644 --- a/packages/e2e-tests/specs/font-size-picker.test.js +++ b/packages/e2e-tests/specs/editor/various/font-size-picker.test.js @@ -75,8 +75,8 @@ describe( 'Font Size Picker', () => { // Clear the custom font size input. await page.click( '.blocks-font-size .components-range-control__number' ); - await pressKeyTimes( 'ArrowRight', 4 ); - await pressKeyTimes( 'Backspace', 4 ); + await pressKeyTimes( 'ArrowRight', 5 ); + await pressKeyTimes( 'Backspace', 5 ); // Ensure content matches snapshot. const content = await getEditedPostContent(); diff --git a/packages/e2e-tests/specs/fullscreen-mode.test.js b/packages/e2e-tests/specs/editor/various/fullscreen-mode.test.js similarity index 100% rename from packages/e2e-tests/specs/fullscreen-mode.test.js rename to packages/e2e-tests/specs/editor/various/fullscreen-mode.test.js diff --git a/packages/e2e-tests/specs/invalid-block.test.js b/packages/e2e-tests/specs/editor/various/invalid-block.test.js similarity index 100% rename from packages/e2e-tests/specs/invalid-block.test.js rename to packages/e2e-tests/specs/editor/various/invalid-block.test.js diff --git a/packages/e2e-tests/specs/keyboard-navigable-blocks.test.js b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js similarity index 100% rename from packages/e2e-tests/specs/keyboard-navigable-blocks.test.js rename to packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js diff --git a/packages/e2e-tests/specs/links.test.js b/packages/e2e-tests/specs/editor/various/links.test.js similarity index 80% rename from packages/e2e-tests/specs/links.test.js rename to packages/e2e-tests/specs/editor/various/links.test.js index f7e78a7b68a1e2..ce9aaeab186799 100644 --- a/packages/e2e-tests/specs/links.test.js +++ b/packages/e2e-tests/specs/editor/various/links.test.js @@ -266,87 +266,6 @@ describe( 'Links', () => { return page.evaluate( () => document.querySelector( '.post-publish-panel__postpublish-post-address input' ).value ); }; - // Test for regressions of https://github.com/WordPress/gutenberg/issues/10496. - it.skip( 'allows autocomplete suggestions to be selected with the mouse', async () => { - // First create a post that we can search for using the link autocompletion. - const titleText = 'Test post mouse'; - const postURL = await createPostWithTitle( titleText ); - - // Now create a new post and try to select the post created previously - // from the autocomplete suggestions. - await createNewPost(); - await clickBlockAppender(); - await page.keyboard.type( 'This is Gutenberg' ); - await pressKeyWithModifier( 'shiftAlt', 'ArrowLeft' ); - await page.click( 'button[aria-label="Link"]' ); - - // Wait for the URL field to auto-focus - await waitForAutoFocus(); - - await page.keyboard.type( titleText ); - const suggestionXPath = `//*[contains(@class, "block-editor-url-input__suggestion")]//button[contains(text(), '${ titleText }')]`; - await page.waitForXPath( suggestionXPath ); - const autocompleteSuggestions = await page.$x( suggestionXPath ); - - // Expect there to be some autocomplete suggestions. - expect( autocompleteSuggestions ).toHaveLength( 1 ); - - const firstSuggestion = autocompleteSuggestions[ 0 ]; - - // Expect that clicking on the autocomplete suggestion doesn't dismiss the link popover. - await firstSuggestion.click(); - expect( await page.$( '.block-editor-url-popover' ) ).not.toBeNull(); - - // Expect the url input value to have been updated with the post url. - const inputValue = await page.evaluate( () => document.querySelector( '.block-editor-url-input input[aria-label="URL"]' ).value ); - expect( inputValue ).toEqual( postURL ); - - // Expect the link to apply correctly. - // Note - have avoided using snapshots here since the link url can't be determined ahead of time. - await page.click( 'button[aria-label="Apply"]' ); - const linkHref = await page.evaluate( () => document.querySelector( '.block-editor-format-toolbar__link-container-value' ).href ); - expect( linkHref ).toEqual( postURL ); - } ); - - // Test for regressions of https://github.com/WordPress/gutenberg/issues/10496. - // This test isn't reliable on Travis and fails from time to time. - // See: https://github.com/WordPress/gutenberg/pull/15211. - it.skip( 'allows autocomplete suggestions to be navigated with the keyboard', async () => { - const titleText = 'Test post keyboard'; - const postURL = await createPostWithTitle( titleText ); - - await createNewPost(); - await clickBlockAppender(); - - // Now in a new post and try to create a link from an autocomplete suggestion using the keyboard. - await page.keyboard.type( 'This is Gutenberg' ); - await pressKeyWithModifier( 'shiftAlt', 'ArrowLeft' ); - - // Press Cmd+K to insert a link - await pressKeyWithModifier( 'primary', 'K' ); - - // Wait for the URL field to auto-focus - await waitForAutoFocus(); - - await page.keyboard.type( titleText ); - await page.waitForSelector( '.block-editor-url-input__suggestion' ); - const autocompleteSuggestions = await page.$x( `//*[contains(@class, "block-editor-url-input__suggestion")]//button[contains(text(), '${ titleText }')]` ); - - // Expect there to be some autocomplete suggestions. - expect( autocompleteSuggestions ).toHaveLength( 1 ); - - // Expect the the first suggestion to be selected when pressing the down arrow. - await page.keyboard.press( 'ArrowDown' ); - const isSelected = await page.evaluate( () => document.querySelector( '.block-editor-url-input__suggestion' ).getAttribute( 'aria-selected' ) ); - expect( isSelected ).toBe( 'true' ); - - // Expect the link to apply correctly when pressing Enter. - // Note - have avoided using snapshots here since the link url can't be determined ahead of time. - await page.keyboard.press( 'Enter' ); - const linkHref = await page.evaluate( () => document.querySelector( '.block-editor-format-toolbar__link-container-value' ).href ); - expect( linkHref ).toEqual( postURL ); - } ); - it( 'allows use of escape key to dismiss the url popover', async () => { const titleText = 'Test post escape'; await createPostWithTitle( titleText ); diff --git a/packages/e2e-tests/specs/manage-reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/manage-reusable-blocks.test.js similarity index 93% rename from packages/e2e-tests/specs/manage-reusable-blocks.test.js rename to packages/e2e-tests/specs/editor/various/manage-reusable-blocks.test.js index 37340d669f5a74..9d17922f519f39 100644 --- a/packages/e2e-tests/specs/manage-reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/manage-reusable-blocks.test.js @@ -32,7 +32,7 @@ describe( 'Managing reusable blocks', () => { await importButton.click(); // Select the file to upload - const testReusableBlockFile = path.join( __dirname, '..', 'assets', 'greeting-reusable-block.json' ); + const testReusableBlockFile = path.join( __dirname, '..', '..', '..', 'assets', 'greeting-reusable-block.json' ); const input = await page.$( '.list-reusable-blocks-import-form input' ); await input.uploadFile( testReusableBlockFile ); diff --git a/packages/e2e-tests/specs/mentions.test.js b/packages/e2e-tests/specs/editor/various/mentions.test.js similarity index 100% rename from packages/e2e-tests/specs/mentions.test.js rename to packages/e2e-tests/specs/editor/various/mentions.test.js diff --git a/packages/e2e-tests/specs/multi-block-selection.test.js b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js similarity index 100% rename from packages/e2e-tests/specs/multi-block-selection.test.js rename to packages/e2e-tests/specs/editor/various/multi-block-selection.test.js diff --git a/packages/e2e-tests/specs/navigable-toolbar.test.js b/packages/e2e-tests/specs/editor/various/navigable-toolbar.test.js similarity index 100% rename from packages/e2e-tests/specs/navigable-toolbar.test.js rename to packages/e2e-tests/specs/editor/various/navigable-toolbar.test.js diff --git a/packages/e2e-tests/specs/new-post-default-content.test.js b/packages/e2e-tests/specs/editor/various/new-post-default-content.test.js similarity index 100% rename from packages/e2e-tests/specs/new-post-default-content.test.js rename to packages/e2e-tests/specs/editor/various/new-post-default-content.test.js diff --git a/packages/e2e-tests/specs/new-post.test.js b/packages/e2e-tests/specs/editor/various/new-post.test.js similarity index 100% rename from packages/e2e-tests/specs/new-post.test.js rename to packages/e2e-tests/specs/editor/various/new-post.test.js diff --git a/packages/e2e-tests/specs/nux.test.js b/packages/e2e-tests/specs/editor/various/nux.test.js similarity index 56% rename from packages/e2e-tests/specs/nux.test.js rename to packages/e2e-tests/specs/editor/various/nux.test.js index 659263cea35335..d3ca54af511763 100644 --- a/packages/e2e-tests/specs/nux.test.js +++ b/packages/e2e-tests/specs/editor/various/nux.test.js @@ -2,10 +2,7 @@ * WordPress dependencies */ import { - clickBlockAppender, - clickOnMoreMenuItem, createNewPost, - saveDraft, toggleScreenOption, } from '@wordpress/e2e-test-utils'; @@ -110,71 +107,4 @@ describe( 'New User Experience (NUX)', () => { areTipsEnabled = await getTipsEnabled( page ); expect( areTipsEnabled ).toEqual( true ); } ); - - // TODO: This test should be enabled once - // https://github.com/WordPress/gutenberg/issues/7458 is fixed. - it.skip( 'should show tips as disabled if all tips have been shown', async () => { - await clickAllTips( page ); - - // Open the "More" menu to check the "Show Tips" element. - await page.click( '.edit-post-more-menu [aria-label="More tools & options"]' ); - const showTipsButton = await page.$x( '//button[contains(text(), "Show Tips")][@aria-pressed="false"]' ); - - expect( showTipsButton ).toHaveLength( 1 ); - } ); - - // TODO: This test should be enabled once - // https://github.com/WordPress/gutenberg/issues/7458 is fixed. - it.skip( 'should reset tips if all tips have been shown and show tips was unchecked', async () => { - const { numberOfTips } = await clickAllTips( page ); - - // Click again to re-enable tips; they should appear. - await clickOnMoreMenuItem( 'Show Tips' ); - - // Open the "More" menu to check the "Show Tips" element. - await page.click( '.edit-post-more-menu [aria-label="More tools & options"]' ); - const showTipsButton = await page.$x( '//button[contains(text(), "Show Tips")][@aria-pressed="true"]' ); - - expect( showTipsButton ).toHaveLength( 1 ); - - // Tips should re-appear on the page. - const nuxTipElements = await page.$$( '.nux-dot-tip' ); - expect( nuxTipElements ).toHaveLength( 1 ); - - // Tips should be enabled again. - const areTipsEnabled = await getTipsEnabled( page ); - expect( areTipsEnabled ).toEqual( true ); - - // Dismissed tips should be reset and ready to be shown again. - const resetTips = await getTips( page ); - const newNumberOfTips = resetTips.tipIds.length; - expect( newNumberOfTips ).toHaveLength( numberOfTips ); - } ); - - // TODO: This test should be enabled once - // https://github.com/WordPress/gutenberg/issues/7753 is fixed. - // See: https://github.com/WordPress/gutenberg/issues/7753#issuecomment-403952816 - it.skip( 'should show tips if "Show tips" was disabled on a draft and then enabled', async () => { - // Click the "Show tips" button (enabled by default) to disable tips. - await clickOnMoreMenuItem( 'Show Tips' ); - - // Let's type something so there's content in this post. - await page.click( '.editor-post-title__input' ); - await page.keyboard.type( 'Post title' ); - await clickBlockAppender(); - await page.keyboard.type( 'Post content goes here.' ); - await saveDraft(); - - // Refresh the page; tips should be disabled. - await page.reload(); - let nuxTipElements = await page.$$( '.nux-dot-tip' ); - expect( nuxTipElements ).toHaveLength( 0 ); - - // Clicking should re-enable tips. - await clickOnMoreMenuItem( 'Show Tips' ); - - // Tips should re-appear on the page. - nuxTipElements = await page.$$( '.nux-dot-tip' ); - expect( nuxTipElements ).toHaveLength( 1 ); - } ); } ); diff --git a/packages/e2e-tests/specs/popovers.test.js b/packages/e2e-tests/specs/editor/various/popovers.test.js similarity index 100% rename from packages/e2e-tests/specs/popovers.test.js rename to packages/e2e-tests/specs/editor/various/popovers.test.js diff --git a/packages/e2e-tests/specs/post-visibility.test.js b/packages/e2e-tests/specs/editor/various/post-visibility.test.js similarity index 100% rename from packages/e2e-tests/specs/post-visibility.test.js rename to packages/e2e-tests/specs/editor/various/post-visibility.test.js diff --git a/packages/e2e-tests/specs/preferences.test.js b/packages/e2e-tests/specs/editor/various/preferences.test.js similarity index 100% rename from packages/e2e-tests/specs/preferences.test.js rename to packages/e2e-tests/specs/editor/various/preferences.test.js diff --git a/packages/e2e-tests/specs/preview.test.js b/packages/e2e-tests/specs/editor/various/preview.test.js similarity index 100% rename from packages/e2e-tests/specs/preview.test.js rename to packages/e2e-tests/specs/editor/various/preview.test.js diff --git a/packages/e2e-tests/specs/publish-button.test.js b/packages/e2e-tests/specs/editor/various/publish-button.test.js similarity index 100% rename from packages/e2e-tests/specs/publish-button.test.js rename to packages/e2e-tests/specs/editor/various/publish-button.test.js diff --git a/packages/e2e-tests/specs/publish-panel.test.js b/packages/e2e-tests/specs/editor/various/publish-panel.test.js similarity index 100% rename from packages/e2e-tests/specs/publish-panel.test.js rename to packages/e2e-tests/specs/editor/various/publish-panel.test.js diff --git a/packages/e2e-tests/specs/publishing.test.js b/packages/e2e-tests/specs/editor/various/publishing.test.js similarity index 100% rename from packages/e2e-tests/specs/publishing.test.js rename to packages/e2e-tests/specs/editor/various/publishing.test.js diff --git a/packages/e2e-tests/specs/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js similarity index 100% rename from packages/e2e-tests/specs/reusable-blocks.test.js rename to packages/e2e-tests/specs/editor/various/reusable-blocks.test.js diff --git a/packages/e2e-tests/specs/rich-text.test.js b/packages/e2e-tests/specs/editor/various/rich-text.test.js similarity index 100% rename from packages/e2e-tests/specs/rich-text.test.js rename to packages/e2e-tests/specs/editor/various/rich-text.test.js diff --git a/packages/e2e-tests/specs/rtl.test.js b/packages/e2e-tests/specs/editor/various/rtl.test.js similarity index 100% rename from packages/e2e-tests/specs/rtl.test.js rename to packages/e2e-tests/specs/editor/various/rtl.test.js diff --git a/packages/e2e-tests/specs/scheduling.test.js b/packages/e2e-tests/specs/editor/various/scheduling.test.js similarity index 100% rename from packages/e2e-tests/specs/scheduling.test.js rename to packages/e2e-tests/specs/editor/various/scheduling.test.js diff --git a/packages/e2e-tests/specs/shortcut-help.test.js b/packages/e2e-tests/specs/editor/various/shortcut-help.test.js similarity index 100% rename from packages/e2e-tests/specs/shortcut-help.test.js rename to packages/e2e-tests/specs/editor/various/shortcut-help.test.js diff --git a/packages/e2e-tests/specs/sidebar-permalink-panel.test.js b/packages/e2e-tests/specs/editor/various/sidebar-permalink-panel.test.js similarity index 100% rename from packages/e2e-tests/specs/sidebar-permalink-panel.test.js rename to packages/e2e-tests/specs/editor/various/sidebar-permalink-panel.test.js diff --git a/packages/e2e-tests/specs/sidebar.test.js b/packages/e2e-tests/specs/editor/various/sidebar.test.js similarity index 99% rename from packages/e2e-tests/specs/sidebar.test.js rename to packages/e2e-tests/specs/editor/various/sidebar.test.js index d0768f916f0f63..c86900f308f1e1 100644 --- a/packages/e2e-tests/specs/sidebar.test.js +++ b/packages/e2e-tests/specs/editor/various/sidebar.test.js @@ -91,6 +91,7 @@ describe( 'Sidebar', () => { await pressKeyWithModifier( 'ctrl', '`' ); await pressKeyWithModifier( 'ctrl', '`' ); await pressKeyWithModifier( 'ctrl', '`' ); + await pressKeyWithModifier( 'ctrl', '`' ); // Tab lands at first (presumed selected) option "Document". await page.keyboard.press( 'Tab' ); diff --git a/packages/e2e-tests/specs/splitting-merging.test.js b/packages/e2e-tests/specs/editor/various/splitting-merging.test.js similarity index 100% rename from packages/e2e-tests/specs/splitting-merging.test.js rename to packages/e2e-tests/specs/editor/various/splitting-merging.test.js diff --git a/packages/e2e-tests/specs/style-variation.test.js b/packages/e2e-tests/specs/editor/various/style-variation.test.js similarity index 100% rename from packages/e2e-tests/specs/style-variation.test.js rename to packages/e2e-tests/specs/editor/various/style-variation.test.js diff --git a/packages/e2e-tests/specs/taxonomies.test.js b/packages/e2e-tests/specs/editor/various/taxonomies.test.js similarity index 83% rename from packages/e2e-tests/specs/taxonomies.test.js rename to packages/e2e-tests/specs/editor/various/taxonomies.test.js index 791fd79b12f8d4..4ed0ef3945fe61 100644 --- a/packages/e2e-tests/specs/taxonomies.test.js +++ b/packages/e2e-tests/specs/editor/various/taxonomies.test.js @@ -1,9 +1,14 @@ +/** + * External dependencies + */ +import { random } from 'lodash'; + /** * WordPress dependencies */ import { - findSidebarPanelWithTitle, createNewPost, + findSidebarPanelWithTitle, openDocumentSettingsSidebar, publishPost, } from '@wordpress/e2e-test-utils'; @@ -50,22 +55,25 @@ describe( 'Taxonomies', () => { }, tagsPanel, TAG_TOKEN_SELECTOR ); }; + const openSidebarPanelWithTitle = async ( title ) => { + const panel = await page.waitForXPath( + `//div[contains(@class,"edit-post-sidebar")]//button[@class="components-button components-panel__body-toggle"][contains(text(),"${ title }")]` + ); + await panel.click(); + }; + it( 'should be able to open the categories panel and create a new main category if the user has the right capabilities', async () => { await createNewPost(); await openDocumentSettingsSidebar(); - const categoriesPanel = await findSidebarPanelWithTitle( 'Categories' ); - expect( categoriesPanel ).toBeDefined(); + await openSidebarPanelWithTitle( 'Categories' ); // If the user has no permission to add a new category finish the test. if ( ! ( await canCreatTermInTaxonomy( 'categories' ) ) ) { return; } - // Open the categories panel. - await categoriesPanel.click( 'button' ); - await page.waitForSelector( 'button.editor-post-taxonomies__hierarchical-terms-add' ); // Click add new category button. @@ -108,27 +116,18 @@ describe( 'Taxonomies', () => { expect( selectedCategories[ 0 ] ).toEqual( 'z rand category 1' ); } ); - // This test isn't reliable locally because repeated execution of the test triggers 400 network - // because of the tag's duplication. Also, it randomly doesn't add a new tag after pressing enter. - // See: https://github.com/WordPress/gutenberg/pull/15211. - it.skip( 'should be able to open the tags panel and create a new tag if the user has the right capabilities', async () => { + it( 'should be able to open the tags panel and create a new tag if the user has the right capabilities', async () => { await createNewPost(); await openDocumentSettingsSidebar(); - const tagsPanel = await findSidebarPanelWithTitle( 'Tags' ); - - //expect( await page.evaluate( ( el ) => el.outerHTML, tagsPanel ) ).toEqual( 'tag1 ok' ); - expect( tagsPanel ).toBeDefined(); + await openSidebarPanelWithTitle( 'Tags' ); // If the user has no permission to add a new tag finish the test. if ( ! ( await canCreatTermInTaxonomy( 'tags' ) ) ) { return; } - // Open the tags panel. - await tagsPanel.click( 'button' ); - // At the start there are no tag tokens expect( await page.$$( @@ -136,13 +135,16 @@ describe( 'Taxonomies', () => { ) ).toHaveLength( 0 ); + const tagsPanel = await findSidebarPanelWithTitle( 'Tags' ); const tagInput = await tagsPanel.$( '.components-form-token-field__input' ); // Click the tag input field. await tagInput.click(); + const tagName = 'tag-' + random( 1, Number.MAX_SAFE_INTEGER ); + // Type the category name in the field. - await tagInput.type( 'tag1' ); + await tagInput.type( tagName ); // Press enter to create a new tag. await tagInput.press( 'Enter' ); @@ -154,7 +156,7 @@ describe( 'Taxonomies', () => { // The post should only contain the tag we added. expect( tags ).toHaveLength( 1 ); - expect( tags[ 0 ] ).toEqual( 'tag1' ); + expect( tags[ 0 ] ).toEqual( tagName ); // Type something in the title so we can publish the post. await page.type( '.editor-post-title__input', 'Hello World' ); @@ -172,6 +174,6 @@ describe( 'Taxonomies', () => { // The tag selection was persisted after the publish process. expect( tags ).toHaveLength( 1 ); - expect( tags[ 0 ] ).toEqual( 'tag1' ); + expect( tags[ 0 ] ).toEqual( tagName ); } ); } ); diff --git a/packages/e2e-tests/specs/typewriter.test.js b/packages/e2e-tests/specs/editor/various/typewriter.test.js similarity index 100% rename from packages/e2e-tests/specs/typewriter.test.js rename to packages/e2e-tests/specs/editor/various/typewriter.test.js diff --git a/packages/e2e-tests/specs/undo.test.js b/packages/e2e-tests/specs/editor/various/undo.test.js similarity index 100% rename from packages/e2e-tests/specs/undo.test.js rename to packages/e2e-tests/specs/editor/various/undo.test.js diff --git a/packages/e2e-tests/specs/writing-flow.test.js b/packages/e2e-tests/specs/editor/various/writing-flow.test.js similarity index 100% rename from packages/e2e-tests/specs/writing-flow.test.js rename to packages/e2e-tests/specs/editor/various/writing-flow.test.js diff --git a/packages/e2e-tests/specs/__snapshots__/block-transforms.test.js.snap b/packages/e2e-tests/specs/experimental/__snapshots__/block-transforms.test.js.snap similarity index 94% rename from packages/e2e-tests/specs/__snapshots__/block-transforms.test.js.snap rename to packages/e2e-tests/specs/experimental/__snapshots__/block-transforms.test.js.snap index dbe341ba42e76e..f461a1a4412dec 100644 --- a/packages/e2e-tests/specs/__snapshots__/block-transforms.test.js.snap +++ b/packages/e2e-tests/specs/experimental/__snapshots__/block-transforms.test.js.snap @@ -20,6 +20,30 @@ exports[`Block transforms correctly transform block Cover in fixture core__cover <!-- /wp:image -->" `; +exports[`Block transforms correctly transform block Cover in fixture core__cover__gradient into the Image block 1`] = ` +"<!-- wp:image --> +<figure class=\\"wp-block-image\\"><img alt=\\"\\"/></figure> +<!-- /wp:image -->" +`; + +exports[`Block transforms correctly transform block Cover in fixture core__cover__gradient into the Video block 1`] = ` +"<!-- wp:video --> +<figure class=\\"wp-block-video\\"></figure> +<!-- /wp:video -->" +`; + +exports[`Block transforms correctly transform block Cover in fixture core__cover__gradient-image into the Image block 1`] = ` +"<!-- wp:image --> +<figure class=\\"wp-block-image\\"><img src=\\"data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=\\" alt=\\"\\"/></figure> +<!-- /wp:image -->" +`; + +exports[`Block transforms correctly transform block Cover in fixture core__cover__gradient-video into the Video block 1`] = ` +"<!-- wp:video --> +<figure class=\\"wp-block-video\\"><video controls src=\\"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=\\"></video></figure> +<!-- /wp:video -->" +`; + exports[`Block transforms correctly transform block Cover in fixture core__cover__video into the Video block 1`] = ` "<!-- wp:video --> <figure class=\\"wp-block-video\\"><video controls src=\\"data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=\\"></video></figure> diff --git a/packages/e2e-tests/specs/block-transforms.test.js b/packages/e2e-tests/specs/experimental/block-transforms.test.js similarity index 88% rename from packages/e2e-tests/specs/block-transforms.test.js rename to packages/e2e-tests/specs/experimental/block-transforms.test.js index 381facd4c00673..bf6e5027b745e6 100644 --- a/packages/e2e-tests/specs/block-transforms.test.js +++ b/packages/e2e-tests/specs/experimental/block-transforms.test.js @@ -13,18 +13,25 @@ import { * WordPress dependencies */ import { + createNewPost, getAllBlocks, getAvailableBlockTransforms, getBlockSetting, getEditedPostContent, hasBlockSwitcher, - createNewPost, - enableExperimentalFeatures, - setPostContent, selectBlockByClientId, + setPostContent, transformBlockTo, } from '@wordpress/e2e-test-utils'; +/** + * Internal dependencies + */ +import { + enableExperimentalFeatures, + disableExperimentalFeatures, +} from '../../experimental-features'; + /** * Internal dependencies */ @@ -32,8 +39,8 @@ import { getAvailableBlockFixturesBasenames, getBlockFixtureHTML, getBlockFixtureParsedJSON, -} from '../fixtures/'; -import { EXPECTED_TRANSFORMS } from '../fixtures/block-transforms.js'; +} from '../../fixtures/'; +import { EXPECTED_TRANSFORMS } from '../../fixtures/block-transforms.js'; /* * Returns true if the fileBase refers to a fixture of a block @@ -100,7 +107,11 @@ describe( 'Block transforms', () => { const transformStructure = {}; beforeAll( async () => { - await enableExperimentalFeatures( [ '#gutenberg-widget-experiments', '#gutenberg-menu-block' ] ); + await enableExperimentalFeatures( [ + '#gutenberg-widget-experiments', + '#gutenberg-menu-block', + '#gutenberg-full-site-editing', + ] ); await createNewPost(); for ( const fileBase of fileBasenames ) { @@ -116,6 +127,16 @@ describe( 'Block transforms', () => { } } ); + afterAll( + async () => { + await disableExperimentalFeatures( [ + '#gutenberg-widget-experiments', + '#gutenberg-menu-block', + '#gutenberg-full-site-editing', + ] ); + } + ); + it( 'should contain the expected transforms', async () => { const transforms = mapValues( pickBy( diff --git a/packages/e2e-tests/specs/demo.test.js b/packages/e2e-tests/specs/local/demo.test.js similarity index 100% rename from packages/e2e-tests/specs/demo.test.js rename to packages/e2e-tests/specs/local/demo.test.js diff --git a/packages/e2e-tests/specs/.gitignore b/packages/e2e-tests/specs/performance/.gitignore similarity index 100% rename from packages/e2e-tests/specs/.gitignore rename to packages/e2e-tests/specs/performance/.gitignore diff --git a/packages/e2e-tests/specs/performance.test.js b/packages/e2e-tests/specs/performance/performance.test.js similarity index 95% rename from packages/e2e-tests/specs/performance.test.js rename to packages/e2e-tests/specs/performance/performance.test.js index 174bf2c6ab1301..6246dfb4dc0d38 100644 --- a/packages/e2e-tests/specs/performance.test.js +++ b/packages/e2e-tests/specs/performance/performance.test.js @@ -20,7 +20,7 @@ function readFile( filePath ) { describe( 'Performance', () => { it( '1000 paragraphs', async () => { - const html = readFile( join( __dirname, '../assets/large-post.html' ) ); + const html = readFile( join( __dirname, '../../assets/large-post.html' ) ); await createNewPost(); await page.evaluate( ( _html ) => { diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index 69dd699b23426e..c1d1576e310bc5 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -1,4 +1,4 @@ -## Master +## 3.8.2 ### Bug Fixes diff --git a/packages/edit-post/README.md b/packages/edit-post/README.md index 3b7b01a53e6dd3..0a9441a55b101f 100644 --- a/packages/edit-post/README.md +++ b/packages/edit-post/README.md @@ -92,13 +92,13 @@ _Parameters_ - _props_ `Object`: Component props. - _props.allowedBlocks_ `[Array]`: An array containing a list of block names for which the item should be shown. If not present, it'll be rendered for any block. If multiple blocks are selected, it'll be shown if and only if all of them are in the whitelist. -- _props.icon_ `[(string|Element)]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element. +- _props.icon_ `[WPBlockTypeIconRender]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element. - _props.label_ `string`: The menu item text. - _props.onClick_ `Function`: Callback function to be executed when the user click the menu item. _Returns_ -- `WPElement`: The WPElement to be rendered. +- `WPComponent`: The component to be rendered. <a name="PluginDocumentSettingPanel" href="#PluginDocumentSettingPanel">#</a> **PluginDocumentSettingPanel** @@ -149,11 +149,11 @@ _Parameters_ - _props.name_ `[string]`: The machine-friendly name for the panel. - _props.className_ `[string]`: An optional class name added to the row. - _props.title_ `[string]`: The title of the panel -- _props.icon_ `[(string|Element)]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. +- _props.icon_ `[WPBlockTypeIconRender]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. _Returns_ -- `WPElement`: The WPElement to be rendered. +- `WPComponent`: The component to be rendered. <a name="PluginMoreMenuItem" href="#PluginMoreMenuItem">#</a> **PluginMoreMenuItem** @@ -206,13 +206,13 @@ _Parameters_ - _props_ `Object`: Component properties. - _props.href_ `[string]`: When `href` is provided then the menu item is represented as an anchor rather than button. It corresponds to the `href` attribute of the anchor. -- _props.icon_ `[(string|Element)]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. +- _props.icon_ `[WPBlockTypeIconRender]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. - _props.onClick_ `[Function]`: The callback function to be executed when the user clicks the menu item. - _props.other_ `[...*]`: Any additional props are passed through to the underlying [MenuItem](/packages/components/src/menu-item/README.md) component. _Returns_ -- `WPElement`: The element to be rendered. +- `WPComponent`: The component to be rendered. <a name="PluginPostPublishPanel" href="#PluginPostPublishPanel">#</a> **PluginPostPublishPanel** @@ -261,11 +261,11 @@ _Parameters_ - _props.className_ `[string]`: An optional class name added to the panel. - _props.title_ `[string]`: Title displayed at the top of the panel. - _props.initialOpen_ `[boolean]`: Whether to have the panel initially opened. When no title is provided it is always opened. -- _props.icon_ `[(string|Element)]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. +- _props.icon_ `[WPBlockTypeIconRender]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. _Returns_ -- `WPElement`: The WPElement to be rendered. +- `WPComponent`: The component to be rendered. <a name="PluginPostStatusInfo" href="#PluginPostStatusInfo">#</a> **PluginPostStatusInfo** @@ -312,7 +312,7 @@ _Parameters_ _Returns_ -- `WPElement`: The WPElement to be rendered. +- `WPComponent`: The component to be rendered. <a name="PluginPrePublishPanel" href="#PluginPrePublishPanel">#</a> **PluginPrePublishPanel** @@ -361,11 +361,11 @@ _Parameters_ - _props.className_ `[string]`: An optional class name added to the panel. - _props.title_ `[string]`: Title displayed at the top of the panel. - _props.initialOpen_ `[boolean]`: Whether to have the panel initially opened. When no title is provided it is always opened. -- _props.icon_ `[(string|Element)]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. +- _props.icon_ `[WPBlockTypeIconRender]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. _Returns_ -- `WPElement`: The WPElement to be rendered. +- `WPComponent`: The component to be rendered. <a name="PluginSidebar" href="#PluginSidebar">#</a> **PluginSidebar** @@ -432,11 +432,11 @@ _Parameters_ - _props.className_ `[string]`: An optional class name added to the sidebar body. - _props.title_ `string`: Title displayed at the top of the sidebar. - _props.isPinnable_ `[boolean]`: Whether to allow to pin sidebar to toolbar. -- _props.icon_ `[(string|Element)]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. +- _props.icon_ `[WPBlockTypeIconRender]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. _Returns_ -- `WPElement`: Plugin sidebar component. +- `WPComponent`: Plugin sidebar component. <a name="PluginSidebarMoreMenuItem" href="#PluginSidebarMoreMenuItem">#</a> **PluginSidebarMoreMenuItem** @@ -482,11 +482,11 @@ _Parameters_ - _props_ `Object`: Component props. - _props.target_ `string`: A string identifying the target sidebar you wish to be activated by this menu item. Must be the same as the `name` prop you have given to that sidebar. -- _props.icon_ `[(string|Element)]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. +- _props.icon_ `[WPBlockTypeIconRender]`: The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. _Returns_ -- `WPElement`: The element to be rendered. +- `WPComponent`: The component to be rendered. <a name="reinitializeEditor" href="#reinitializeEditor">#</a> **reinitializeEditor** diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 98fd08247be8f0..a73c2d52ac105b 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "3.8.0", + "version": "3.8.3", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js index 8503692c289b07..938805e057406b 100644 --- a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js +++ b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js @@ -35,7 +35,7 @@ const shouldRenderItem = ( selectedBlocks, allowedBlocks ) => ! Array.isArray( a * * @param {Object} props Component props. * @param {Array} [props.allowedBlocks] An array containing a list of block names for which the item should be shown. If not present, it'll be rendered for any block. If multiple blocks are selected, it'll be shown if and only if all of them are in the whitelist. - * @param {string|Element} [props.icon] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element. + * @param {WPBlockTypeIconRender} [props.icon] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element. * @param {string} props.label The menu item text. * @param {Function} props.onClick Callback function to be executed when the user click the menu item. * @@ -81,7 +81,7 @@ const shouldRenderItem = ( selectedBlocks, allowedBlocks ) => ! Array.isArray( a * ); * ``` * - * @return {WPElement} The WPElement to be rendered. + * @return {WPComponent} The component to be rendered. */ const PluginBlockSettingsMenuItem = ( { allowedBlocks, icon, label, onClick, small, role } ) => ( <PluginBlockSettingsMenuGroup> diff --git a/packages/edit-post/src/components/header/plugin-more-menu-item/index.js b/packages/edit-post/src/components/header/plugin-more-menu-item/index.js index e29ec22e90a208..08a5bef83b35eb 100644 --- a/packages/edit-post/src/components/header/plugin-more-menu-item/index.js +++ b/packages/edit-post/src/components/header/plugin-more-menu-item/index.js @@ -32,7 +32,7 @@ const PluginMoreMenuItem = ( { onClick = noop, ...props } ) => ( * * @param {Object} props Component properties. * @param {string} [props.href] When `href` is provided then the menu item is represented as an anchor rather than button. It corresponds to the `href` attribute of the anchor. - * @param {string|Element} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. * @param {Function} [props.onClick=noop] The callback function to be executed when the user clicks the menu item. * @param {...*} [props.other] Any additional props are passed through to the underlying [MenuItem](/packages/components/src/menu-item/README.md) component. * @@ -78,7 +78,7 @@ const PluginMoreMenuItem = ( { onClick = noop, ...props } ) => ( * ); * ``` * - * @return {WPElement} The element to be rendered. + * @return {WPComponent} The component to be rendered. */ export default compose( withPluginContext( ( context, ownProps ) => { diff --git a/packages/edit-post/src/components/header/plugin-sidebar-more-menu-item/index.js b/packages/edit-post/src/components/header/plugin-sidebar-more-menu-item/index.js index e1849f2e7890ed..c1f4ff42ae98aa 100644 --- a/packages/edit-post/src/components/header/plugin-sidebar-more-menu-item/index.js +++ b/packages/edit-post/src/components/header/plugin-sidebar-more-menu-item/index.js @@ -28,7 +28,7 @@ const PluginSidebarMoreMenuItem = ( { children, icon, isSelected, onClick } ) => * * @param {Object} props Component props. * @param {string} props.target A string identifying the target sidebar you wish to be activated by this menu item. Must be the same as the `name` prop you have given to that sidebar. - * @param {string|Element} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered to the left of the menu item label. * * @example <caption>ES5</caption> * ```js @@ -64,7 +64,7 @@ const PluginSidebarMoreMenuItem = ( { children, icon, isSelected, onClick } ) => * ); * ``` * - * @return {WPElement} The element to be rendered. + * @return {WPComponent} The component to be rendered. */ export default compose( withPluginContext( ( context, ownProps ) => { diff --git a/packages/edit-post/src/components/header/style.scss b/packages/edit-post/src/components/header/style.scss index 64699eeab73a2d..65a94ba8ad72d0 100644 --- a/packages/edit-post/src/components/header/style.scss +++ b/packages/edit-post/src/components/header/style.scss @@ -91,7 +91,7 @@ &.editor-post-publish-button, &.editor-post-publish-panel__toggle { margin: 2px; - height: 33px; + height: 34px; line-height: 32px; font-size: $default-font-size; } @@ -105,13 +105,17 @@ } } + // These paddings actually duplicate existing rules from button component. + // But they are duplicated so as to provide smaller paddings to fit the buttons on mobile. &.editor-post-preview, &.editor-post-publish-button, &.editor-post-publish-panel__toggle { - padding: 0 5px 2px; + padding-left: 5px; + padding-right: 5px; @include break-small() { - padding: 0 12px 2px; + padding-left: 12px; + padding-right: 12px; } } diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index e29011c8cec529..b8cb779603410c 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -21,6 +21,7 @@ import { EditorNotices, PostPublishPanel, } from '@wordpress/editor'; +import { BlockBreadcrumb } from '@wordpress/block-editor'; import { withDispatch, withSelect } from '@wordpress/data'; import { PluginArea } from '@wordpress/plugins'; import { withViewportMatch } from '@wordpress/viewport'; @@ -59,7 +60,7 @@ function Layout( { } ) { const sidebarIsOpened = editorSidebarOpened || pluginSidebarOpened || publishSidebarOpened; - const className = classnames( 'edit-post-layout', { + const className = classnames( 'edit-post-layout', 'is-mode-' + mode, { 'is-sidebar-opened': sidebarIsOpened, 'has-fixed-toolbar': hasFixedToolbar, 'has-metaboxes': hasActiveMetaboxes, @@ -80,7 +81,7 @@ function Layout( { <LocalAutosaveMonitor /> <Header /> <div - className="edit-post-layout__content" + className="edit-post-layout__content edit-post-layout__scrollable-container" role="region" /* translators: accessibility text for the content landmark region. */ aria-label={ __( 'Editor content' ) } @@ -99,7 +100,19 @@ function Layout( { <div className="edit-post-layout__metaboxes"> <MetaBoxes location="advanced" /> </div> + { isMobileViewport && sidebarIsOpened && <ScrollLock /> } </div> + { isRichEditingEnabled && mode === 'visual' && ( + <div + className="edit-post-layout__footer" + role="region" + /* translators: accessibility text for the content landmark region. */ + aria-label={ __( 'Editor footer' ) } + tabIndex="-1" + > + <BlockBreadcrumb /> + </div> + ) } { publishSidebarOpened ? ( <PostPublishPanel { ...publishLandmarkProps } @@ -124,9 +137,6 @@ function Layout( { </div> <SettingsSidebar /> <Sidebar.Slot /> - { - isMobileViewport && sidebarIsOpened && <ScrollLock /> - } </> ) } <Popover.Slot /> diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 43996d98f8c8f0..40e49f9df3b355 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -54,7 +54,7 @@ // Because the body element scrolls the navigation sidebar, we have to use position fixed here. // Otherwise you would scroll the editing canvas out of view when you scroll the sidebar. position: fixed; - bottom: 0; + bottom: $footer-height; left: 0; right: 0; @@ -70,6 +70,12 @@ // Because we are beyond the medium breakpoint, we only have to worry about folded, auto-folded, and default. margin-left: $admin-sidebar-width; + // Make room for the footer + .edit-post-layout.is-mode-visual & { + bottom: $footer-height; + min-height: calc(100% - #{ $header-height + $admin-bar-height + $footer-height }); + } + // Auto fold is when on smaller breakpoints, nav menu auto colllapses. body.auto-fold & { margin-left: $admin-sidebar-width-collapsed; @@ -104,31 +110,6 @@ } } - // Pad the scroll box so content on the bottom can be scrolled up. - padding-bottom: 50vh; - @include break-small { - padding-bottom: 0; - } - - // On mobile the main content (html or body) area has to scroll. - // If, like we do on the desktop, scroll an element (.edit-post-layout__content) you can invoke - // the overscroll bounce on the non-scrolling container, causing for a frustrating scrolling experience. - // The following rule enables this scrolling beyond the mobile breakpoint, because on the desktop - // breakpoints scrolling an isolated element helps avoid scroll bleed. - @include break-small() { - overflow-y: auto; - } - -webkit-overflow-scrolling: touch; - - // This rule ensures that if you've scrolled to the end of a container, - // then pause, then keep scrolling downwards, the browser doesn't try to scroll - // the parent element, usually invoking a "bounce" effect and then preventing you - // from scrolling upwards until you pause again. - // This is only necessary beyond the small breakpoint because that's when the scroll container changes. - @include break-small() { - overscroll-behavior-y: none; - } - .edit-post-visual-editor { flex: 1 1 auto; @@ -234,3 +215,51 @@ } } } + +.edit-post-layout__footer { + display: none; + z-index: z-index(".edit-post-layout__footer"); + + // Stretch to mimic outline padding on desktop. + @include break-medium() { + display: flex; + position: fixed; + bottom: 0; + right: 0; + background: $white; + height: $footer-height; + padding: 0 $grid-size; + align-items: center; + border-top: $border-width solid $light-gray-500; + font-size: $default-font-size; + box-sizing: border-box; + } +} +@include editor-left(".edit-post-layout__footer"); + +.edit-post-layout__scrollable-container { + // On mobile the main content (html or body) area has to scroll. + // If, like we do on the desktop, scroll an element (.edit-post-layout__content) you can invoke + // the overscroll bounce on the non-scrolling container, causing for a frustrating scrolling experience. + // The following rule enables this scrolling beyond the mobile breakpoint, because on the desktop + // breakpoints scrolling an isolated element helps avoid scroll bleed. + @include break-small() { + overflow-y: auto; + } + -webkit-overflow-scrolling: touch; + + // This rule ensures that if you've scrolled to the end of a container, + // then pause, then keep scrolling downwards, the browser doesn't try to scroll + // the parent element, usually invoking a "bounce" effect and then preventing you + // from scrolling upwards until you pause again. + // This is only necessary beyond the small breakpoint because that's when the scroll container changes. + @include break-small() { + overscroll-behavior-y: none; + } + + // Pad the scroll box so content on the bottom can be scrolled up. + padding-bottom: 50vh; + @include break-small { + padding-bottom: 0; + } +} diff --git a/packages/edit-post/src/components/meta-boxes/meta-boxes-area/style.scss b/packages/edit-post/src/components/meta-boxes/meta-boxes-area/style.scss index b86d15952683b0..184835a024c35c 100644 --- a/packages/edit-post/src/components/meta-boxes/meta-boxes-area/style.scss +++ b/packages/edit-post/src/components/meta-boxes/meta-boxes-area/style.scss @@ -81,6 +81,25 @@ .is-hidden { display: none; } + + + // Until checkboxes WordPress-wide are updated to match the new style, + // checkboxes used in metaboxes have to be slightly unstyled here. + // @todo: remove this entire rule once checkboxes are the same everywhere. + // See: https://github.com/WordPress/gutenberg/issues/18053 + .postbox-container .postbox input[type="checkbox"], + .postbox-container .postbox input[type="radio"] { + border: $border-width solid $dark-gray-300; + + &:checked { + background: $white; + border-color: $dark-gray-300; + } + + &::before { + margin: -3px -4px; + } + } } .edit-post-meta-boxes-area__clear { diff --git a/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js b/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js index ef10b4ed77ef30..62a651c7f0c24e 100644 --- a/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js +++ b/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js @@ -48,7 +48,7 @@ const PluginDocumentSettingFill = ( { isEnabled, panelName, opened, onToggle, cl * @param {string} [props.name] The machine-friendly name for the panel. * @param {string} [props.className] An optional class name added to the row. * @param {string} [props.title] The title of the panel - * @param {string|Element} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. * * @example <caption>ES5</caption> * ```js @@ -89,7 +89,7 @@ const PluginDocumentSettingFill = ( { isEnabled, panelName, opened, onToggle, cl * registerPlugin( 'document-setting-test', { render: MyDocumentSettingTest } ); * ``` * - * @return {WPElement} The WPElement to be rendered. + * @return {WPComponent} The component to be rendered. */ const PluginDocumentSettingPanel = compose( withPluginContext( ( context, ownProps ) => { diff --git a/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/index.js b/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/index.js index a84124905d7d46..b1c53372857023 100644 --- a/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/index.js +++ b/packages/edit-post/src/components/sidebar/plugin-post-publish-panel/index.js @@ -27,7 +27,7 @@ const PluginPostPublishPanelFill = ( { children, className, title, initialOpen = * @param {string} [props.className] An optional class name added to the panel. * @param {string} [props.title] Title displayed at the top of the panel. * @param {boolean} [props.initialOpen=false] Whether to have the panel initially opened. When no title is provided it is always opened. - * @param {string|Element} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. * * @example <caption>ES5</caption> * ```js @@ -65,7 +65,7 @@ const PluginPostPublishPanelFill = ( { children, className, title, initialOpen = * ); * ``` * - * @return {WPElement} The WPElement to be rendered. + * @return {WPComponent} The component to be rendered. */ const PluginPostPublishPanel = compose( diff --git a/packages/edit-post/src/components/sidebar/plugin-post-status-info/index.js b/packages/edit-post/src/components/sidebar/plugin-post-status-info/index.js index e099fa232e84f6..2d149639130eda 100644 --- a/packages/edit-post/src/components/sidebar/plugin-post-status-info/index.js +++ b/packages/edit-post/src/components/sidebar/plugin-post-status-info/index.js @@ -49,7 +49,7 @@ export const { Fill, Slot } = createSlotFill( 'PluginPostStatusInfo' ); * ); * ``` * - * @return {WPElement} The WPElement to be rendered. + * @return {WPComponent} The component to be rendered. */ const PluginPostStatusInfo = ( { children, className } ) => ( <Fill> diff --git a/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/index.js b/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/index.js index 4a3b02fba2803b..13863b24f16d50 100644 --- a/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/index.js +++ b/packages/edit-post/src/components/sidebar/plugin-pre-publish-panel/index.js @@ -18,16 +18,19 @@ const PluginPrePublishPanelFill = ( { children, className, title, initialOpen = </PanelBody> </Fill> ); + /** * Renders provided content to the pre-publish side panel in the publish flow * (side panel that opens when a user first pushes "Publish" from the main editor). * - * @param {Object} props Component props. - * @param {string} [props.className] An optional class name added to the panel. - * @param {string} [props.title] Title displayed at the top of the panel. - * @param {boolean} [props.initialOpen=false] Whether to have the panel initially opened. When no title is provided it is always opened. - * @param {string|Element} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. - + * @param {Object} props Component props. + * @param {string} [props.className] An optional class name added to the panel. + * @param {string} [props.title] Title displayed at the top of the panel. + * @param {boolean} [props.initialOpen=false] Whether to have the panel initially opened. + * When no title is provided it is always opened. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) + * icon slug string, or an SVG WP element, to be rendered when + * the sidebar is pinned to toolbar. * * @example <caption>ES5</caption> * ```js @@ -65,7 +68,7 @@ const PluginPrePublishPanelFill = ( { children, className, title, initialOpen = * ); * ``` * - * @return {WPElement} The WPElement to be rendered. + * @return {WPComponent} The component to be rendered. */ const PluginPrePublishPanel = compose( withPluginContext( ( context, ownProps ) => { diff --git a/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js b/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js index 5303de703b30b9..7c03f172b009fb 100644 --- a/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js @@ -82,7 +82,7 @@ function PluginSidebar( props ) { * @param {string} [props.className] An optional class name added to the sidebar body. * @param {string} props.title Title displayed at the top of the sidebar. * @param {boolean} [props.isPinnable=true] Whether to allow to pin sidebar to toolbar. - * @param {string|Element} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. + * @param {WPBlockTypeIconRender} [props.icon=inherits from the plugin] The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug string, or an SVG WP element, to be rendered when the sidebar is pinned to toolbar. * * @example <caption>ES5</caption> * ```js @@ -129,7 +129,7 @@ function PluginSidebar( props ) { * ); * ``` * - * @return {WPElement} Plugin sidebar component. + * @return {WPComponent} Plugin sidebar component. */ export default compose( withPluginContext( ( context, ownProps ) => { diff --git a/packages/edit-post/src/components/sidebar/post-slug/index.js b/packages/edit-post/src/components/sidebar/post-slug/index.js new file mode 100644 index 00000000000000..fe539a38decce6 --- /dev/null +++ b/packages/edit-post/src/components/sidebar/post-slug/index.js @@ -0,0 +1,17 @@ +/** + * WordPress dependencies + */ +import { PanelRow } from '@wordpress/components'; +import { PostSlug as PostSlugForm, PostSlugCheck } from '@wordpress/editor'; + +export function PostSlug() { + return ( + <PostSlugCheck> + <PanelRow> + <PostSlugForm /> + </PanelRow> + </PostSlugCheck> + ); +} + +export default PostSlug; diff --git a/packages/edit-post/src/components/sidebar/post-slug/style.scss b/packages/edit-post/src/components/sidebar/post-slug/style.scss new file mode 100644 index 00000000000000..b031424164dcec --- /dev/null +++ b/packages/edit-post/src/components/sidebar/post-slug/style.scss @@ -0,0 +1,4 @@ +.editor-post-slug__input { + margin: -5px 0; + padding: 2px; +} diff --git a/packages/edit-post/src/components/sidebar/post-status/index.js b/packages/edit-post/src/components/sidebar/post-status/index.js index a3403a8209c9e5..b9d50676f7141a 100644 --- a/packages/edit-post/src/components/sidebar/post-status/index.js +++ b/packages/edit-post/src/components/sidebar/post-status/index.js @@ -14,6 +14,7 @@ import PostTrash from '../post-trash'; import PostSchedule from '../post-schedule'; import PostSticky from '../post-sticky'; import PostAuthor from '../post-author'; +import PostSlug from '../post-slug'; import PostFormat from '../post-format'; import PostPendingStatus from '../post-pending-status'; import PluginPostStatusInfo from '../plugin-post-status-info'; @@ -34,6 +35,7 @@ function PostStatus( { isOpened, onTogglePanel } ) { <PostFormat /> <PostSticky /> <PostPendingStatus /> + <PostSlug /> <PostAuthor /> { fills } <PostTrash /> diff --git a/packages/edit-post/src/components/sidebar/style.scss b/packages/edit-post/src/components/sidebar/style.scss index 740355cf636f75..50ddd2efedbde5 100644 --- a/packages/edit-post/src/components/sidebar/style.scss +++ b/packages/edit-post/src/components/sidebar/style.scss @@ -22,6 +22,10 @@ @include break-medium() { top: $admin-bar-height + $header-height; + .edit-post-layout.is-mode-visual & { + bottom: $footer-height; + } + body.is-fullscreen-mode & { top: $header-height; } diff --git a/packages/edit-post/src/components/text-editor/style.scss b/packages/edit-post/src/components/text-editor/style.scss index 234eb46917b247..f7696bb4ee1959 100644 --- a/packages/edit-post/src/components/text-editor/style.scss +++ b/packages/edit-post/src/components/text-editor/style.scss @@ -1,30 +1,5 @@ -.edit-post-text-editor__body { - padding-top: $grid-size * 5; - - @include break-small() { - padding-top: ($grid-size * 5) + $admin-bar-height-big; - } - - @include break-medium() { - padding-top: $grid-size * 5; - - body.is-fullscreen-mode & { - padding-top: $grid-size * 5; - } - } -} - .edit-post-text-editor { width: 100%; - max-width: calc(100% - #{$grid-size-large * 2}); - margin-left: $grid-size-large; - margin-right: $grid-size-large; - - @include break-small() { - max-width: $content-width; - margin-left: auto; - margin-right: auto; - } // Always show outlines in code editor .editor-post-title__block { @@ -67,34 +42,41 @@ } } - .editor-post-text-editor { - padding: $block-padding; - min-height: 200px; - line-height: 1.8; - } - // Make room for toolbar. padding-top: $block-controls-height + $grid-size; +} - // Exit Code Editor toolbar. - .edit-post-text-editor__toolbar { - position: absolute; - top: $grid-size; - left: 0; - right: 0; - height: $block-controls-height; - line-height: $block-controls-height; - padding: 0 $grid-size 0 $grid-size-large; - display: flex; +// Exit Code Editor toolbar. +.edit-post-text-editor__toolbar { + position: absolute; + top: $grid-size; + left: 0; + right: 0; + height: $block-controls-height; + line-height: $block-controls-height; + padding: 0 $grid-size 0 $grid-size-large; + display: flex; + + h2 { + margin: 0 auto 0 0; + font-size: $default-font-size; + color: $dark-gray-500; + } - h2 { - margin: 0 auto 0 0; - font-size: $default-font-size; - color: $dark-gray-500; - } + .components-icon-button svg { + order: 1; + } +} - .components-icon-button svg { - order: 1; - } +.edit-post-text-editor__body { + max-width: calc(100% - #{$grid-size-large * 2}); + margin-left: $grid-size-large; + margin-right: $grid-size-large; + padding-top: $grid-size * 5; + + @include break-small() { + max-width: $content-width; + margin-left: auto; + margin-right: auto; } } 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 ea2958949f7e2d..54a502603c2064 100644 --- a/packages/edit-post/src/hooks/validate-multiple-use/index.js +++ b/packages/edit-post/src/hooks/validate-multiple-use/index.js @@ -28,9 +28,9 @@ const enhance = compose( * "original" block is not the current one. Thus, an inexisting * `originalBlockClientId` prop signals that the block is valid. * - * @param {Component} WrappedBlockEdit A filtered BlockEdit instance. + * @param {WPComponent} WrappedBlockEdit A filtered BlockEdit instance. * - * @return {Component} Enhanced component with merged state data props. + * @return {WPComponent} Enhanced component with merged state data props. */ withSelect( ( select, block ) => { const multiple = hasBlockSupport( block.name, 'multiple', true ); diff --git a/packages/edit-post/src/store/selectors.js b/packages/edit-post/src/store/selectors.js index 2430ee41087232..fff04ac721fe37 100644 --- a/packages/edit-post/src/store/selectors.js +++ b/packages/edit-post/src/store/selectors.js @@ -78,9 +78,9 @@ export function getPreferences( state ) { * * @param {Object} state Global application state. * @param {string} preferenceKey Preference Key. - * @param {Mixed} defaultValue Default Value. + * @param {*} defaultValue Default Value. * - * @return {Mixed} Preference Value. + * @return {*} Preference Value. */ export function getPreference( state, preferenceKey, defaultValue ) { const preferences = getPreferences( state ); diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index 6403380ff54601..94fcfa7ac60fe6 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -1,3 +1,5 @@ +$footer-height: $icon-button-size-small; + @import "./components/fullscreen-mode/style.scss"; @import "./components/header/style.scss"; @import "./components/header/fullscreen-mode-close/style.scss"; @@ -13,6 +15,7 @@ @import "./components/sidebar/post-author/style.scss"; @import "./components/sidebar/post-link/style.scss"; @import "./components/sidebar/post-schedule/style.scss"; +@import "./components/sidebar/post-slug/style.scss"; @import "./components/sidebar/post-status/style.scss"; @import "./components/sidebar/post-visibility/style.scss"; @import "./components/sidebar/settings-header/style.scss"; diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index e5ba0beff046e2..37dd6972b49627 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-widgets", - "version": "0.7.0", + "version": "0.7.3", "private": true, "description": "Widgets Page module for WordPress..", "author": "The WordPress Contributors", diff --git a/packages/edit-widgets/src/components/widget-area/index.js b/packages/edit-widgets/src/components/widget-area/index.js index e22c880f50cf22..11e162d2874d9d 100644 --- a/packages/edit-widgets/src/components/widget-area/index.js +++ b/packages/edit-widgets/src/components/widget-area/index.js @@ -42,7 +42,7 @@ function getBlockEditorSettings( blockEditorSettings, hasUploadPermissions ) { }; return { ...blockEditorSettings, - __experimentalMediaUpload: mediaUploadBlockEditor, + mediaUpload: mediaUploadBlockEditor, }; } diff --git a/packages/editor/package.json b/packages/editor/package.json index 22e78a023ca3f6..9ddc2a6d1951ae 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "9.7.0", + "version": "9.7.3", "description": "Building blocks for WordPress editors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/src/components/autocompleters/block.js b/packages/editor/src/components/autocompleters/block.js index 70860643ae096b..5311d01ec2b749 100644 --- a/packages/editor/src/components/autocompleters/block.js +++ b/packages/editor/src/components/autocompleters/block.js @@ -27,7 +27,7 @@ function defaultGetBlockInsertionParentClientId() { * @param {string} rootClientId Client ID of the block for which to retrieve * inserter items. * - * @return {Array<Editor.InserterItem>} The inserter items for the specified + * @return {Array<WPEditorInserterItem>} The inserter items for the specified * parent. */ function defaultGetInserterItems( rootClientId ) { @@ -68,7 +68,7 @@ const fetchReusableBlocks = once( () => { /** * Creates a blocks repeater for replacing the current block with a selected block type. * - * @return {Completer} A blocks completer. + * @return {WPCompleter} A blocks completer. */ export function createBlockCompleter( { // Allow store-based selectors to be overridden for unit test. @@ -119,6 +119,6 @@ export function createBlockCompleter( { /** * Creates a blocks repeater for replacing the current block with a selected block type. * - * @return {Completer} A blocks completer. + * @return {WPCompleter} A blocks completer. */ export default createBlockCompleter(); diff --git a/packages/editor/src/components/autocompleters/user.js b/packages/editor/src/components/autocompleters/user.js index e3965a33f28d6f..1dea163ee16443 100644 --- a/packages/editor/src/components/autocompleters/user.js +++ b/packages/editor/src/components/autocompleters/user.js @@ -6,7 +6,7 @@ import apiFetch from '@wordpress/api-fetch'; /** * A user mentions completer. * - * @type {Completer} + * @type {WPCompleter} */ export default { name: 'users', diff --git a/packages/editor/src/components/index.js b/packages/editor/src/components/index.js index fed6f633ffbfd0..d5af7f4ff443bf 100644 --- a/packages/editor/src/components/index.js +++ b/packages/editor/src/components/index.js @@ -42,6 +42,8 @@ export { default as PostSavedState } from './post-saved-state'; export { default as PostSchedule } from './post-schedule'; export { default as PostScheduleCheck } from './post-schedule/check'; export { default as PostScheduleLabel } from './post-schedule/label'; +export { default as PostSlug } from './post-slug'; +export { default as PostSlugCheck } from './post-slug/check'; export { default as PostSticky } from './post-sticky'; export { default as PostStickyCheck } from './post-sticky/check'; export { default as PostSwitchToDraftButton } from './post-switch-to-draft-button'; diff --git a/packages/editor/src/components/local-autosave-monitor/index.js b/packages/editor/src/components/local-autosave-monitor/index.js index 59be30218f20cd..9dbd94aab5f56d 100644 --- a/packages/editor/src/components/local-autosave-monitor/index.js +++ b/packages/editor/src/components/local-autosave-monitor/index.js @@ -130,7 +130,12 @@ function useAutosavePurge() { const lastIsAutosaving = useRef( isAutosaving ); useEffect( () => { - if ( lastIsAutosaving.current && ! isAutosaving && ! didError ) { + if ( + ! didError && ( + ( lastIsAutosaving.current && ! isAutosaving ) || + ( lastIsDirty.current && ! isDirty ) + ) + ) { localAutosaveClear( postId ); } diff --git a/packages/editor/src/components/post-publish-button/index.js b/packages/editor/src/components/post-publish-button/index.js index 1513dfbfbb414f..ac195d4de86ce6 100644 --- a/packages/editor/src/components/post-publish-button/index.js +++ b/packages/editor/src/components/post-publish-button/index.js @@ -113,6 +113,7 @@ export class PostPublishButton extends Component { return ( <div> <Button + isLarge ref={ this.buttonNode } { ...componentProps } > diff --git a/packages/editor/src/components/post-slug/check.js b/packages/editor/src/components/post-slug/check.js new file mode 100644 index 00000000000000..6b243bd6b9f641 --- /dev/null +++ b/packages/editor/src/components/post-slug/check.js @@ -0,0 +1,10 @@ +/** + * Internal dependencies + */ +import PostTypeSupportCheck from '../post-type-support-check'; + +export default function PostSlugCheck( { children } ) { + return ( + <PostTypeSupportCheck supportKeys="slug">{ children }</PostTypeSupportCheck> + ); +} diff --git a/packages/editor/src/components/post-slug/index.js b/packages/editor/src/components/post-slug/index.js new file mode 100644 index 00000000000000..9a9b2200e72be0 --- /dev/null +++ b/packages/editor/src/components/post-slug/index.js @@ -0,0 +1,85 @@ +/** + * WordPress dependencies + */ +import { withDispatch, withSelect } from '@wordpress/data'; +import { Component } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { withInstanceId, compose } from '@wordpress/compose'; +import { safeDecodeURIComponent } from '@wordpress/url'; + +/** + * Internal dependencies + */ +import PostSlugCheck from './check'; +import { cleanForSlug } from '../../utils/url'; + +export class PostSlug extends Component { + constructor( { postSlug, postTitle, postID } ) { + super( ...arguments ); + + this.state = { + editedSlug: safeDecodeURIComponent( postSlug ) || cleanForSlug( postTitle ) || postID, + }; + + this.setSlug = this.setSlug.bind( this ); + } + + setSlug( event ) { + const { postSlug, onUpdateSlug } = this.props; + const { value } = event.target; + + const editedSlug = cleanForSlug( value ); + + if ( editedSlug === postSlug ) { + return; + } + + onUpdateSlug( editedSlug ); + } + + render() { + const { instanceId } = this.props; + const { editedSlug } = this.state; + + const inputId = 'editor-post-slug-' + instanceId; + + return ( + <PostSlugCheck> + <label htmlFor={ inputId }>{ __( 'Slug' ) }</label> + <input + type="text" + id={ inputId } + value={ editedSlug } + onChange={ ( event ) => this.setState( { editedSlug: event.target.value } ) } + onBlur={ this.setSlug } + className="editor-post-slug__input" + /> + </PostSlugCheck> + ); + } +} + +export default compose( [ + withSelect( ( select ) => { + const { + getCurrentPost, + getEditedPostAttribute, + } = select( 'core/editor' ); + + const { id } = getCurrentPost(); + return { + postSlug: getEditedPostAttribute( 'slug' ), + postTitle: getEditedPostAttribute( 'title' ), + postID: id, + }; + } ), + withDispatch( ( dispatch ) => { + const { editPost } = dispatch( 'core/editor' ); + return { + onUpdateSlug( slug ) { + editPost( { slug } ); + }, + }; + } ), + withInstanceId, +] )( PostSlug ); diff --git a/packages/editor/src/components/post-slug/test/check.js b/packages/editor/src/components/post-slug/test/check.js new file mode 100644 index 00000000000000..90fe356236adbc --- /dev/null +++ b/packages/editor/src/components/post-slug/test/check.js @@ -0,0 +1,21 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; + +/** + * Internal dependencies + */ +import PostSlugCheck from '../check'; + +describe( 'PostSlugCheck', () => { + it( 'should render control', () => { + const wrapper = shallow( + <PostSlugCheck> + slug + </PostSlugCheck> + ); + + expect( wrapper.type() ).not.toBe( null ); + } ); +} ); diff --git a/packages/editor/src/components/post-slug/test/index.js b/packages/editor/src/components/post-slug/test/index.js new file mode 100644 index 00000000000000..cf25f25e0fe377 --- /dev/null +++ b/packages/editor/src/components/post-slug/test/index.js @@ -0,0 +1,45 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; + +/** + * Internal dependencies + */ +import { PostSlug } from '../'; + +describe( 'PostSlug', () => { + describe( '#render()', () => { + it( 'should update internal slug', () => { + const wrapper = shallow( + <PostSlug + postSlug="index" /> + ); + + wrapper.find( 'input' ).simulate( 'change', { + target: { + value: 'single-post', + }, + } ); + + expect( wrapper.state().editedSlug ).toEqual( 'single-post' ); + } ); + + it( 'should update slug', () => { + const onUpdateSlug = jest.fn(); + const wrapper = shallow( + <PostSlug + postSlug="index" + onUpdateSlug={ onUpdateSlug } /> + ); + + wrapper.find( 'input' ).simulate( 'blur', { + target: { + value: 'single-post', + }, + } ); + + expect( onUpdateSlug ).toHaveBeenCalledWith( 'single-post' ); + } ); + } ); +} ); diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index 3b4c1c8a0c2926..06db56eef101f5 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -128,13 +128,13 @@ class FlatTermSelector extends Component { } updateSelectedTerms( terms = [] ) { - const selectedTerms = terms.reduce( ( result, termId ) => { + const selectedTerms = terms.reduce( ( accumulator, termId ) => { const termObject = find( this.state.availableTerms, ( term ) => term.id === termId ); if ( termObject ) { - result.push( termObject.name ); + accumulator.push( termObject.name ); } - return result; + return accumulator; }, [] ); this.setState( { selectedTerms, diff --git a/packages/editor/src/components/post-type-support-check/index.js b/packages/editor/src/components/post-type-support-check/index.js index 8096e4ccf7f8d9..7aae5e6abb0362 100644 --- a/packages/editor/src/components/post-type-support-check/index.js +++ b/packages/editor/src/components/post-type-support-check/index.js @@ -12,14 +12,14 @@ import { withSelect } from '@wordpress/data'; * A component which renders its own children only if the current editor post * type supports one of the given `supportKeys` prop. * - * @param {Object} props - * @param {string} [props.postType] Current post type. - * @param {WPElement} props.children Children to be rendered if post - * type supports. - * @param {(string|string[])} props.supportKeys String or string array of keys - * to test. + * @param {Object} props Props. + * @param {string} [props.postType] Current post type. + * @param {WPElement} props.children Children to be rendered if post + * type supports. + * @param {(string|string[])} props.supportKeys String or string array of keys + * to test. * - * @return {WPElement} Rendered element. + * @return {WPComponent} The component to be rendered. */ export function PostTypeSupportCheck( { postType, children, supportKeys } ) { let isSupported = true; diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index c386f02097167b..37eb46ff235b52 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, pick, defaultTo, differenceBy, isEqual, noop } from 'lodash'; +import { map, pick, defaultTo } from 'lodash'; import memize from 'memize'; /** @@ -16,7 +16,6 @@ import { BlockEditorProvider, transformStyles } from '@wordpress/block-editor'; import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; import { decodeEntities } from '@wordpress/html-entities'; -import { unregisterBlockType } from '@wordpress/blocks'; /** * Internal dependencies @@ -25,7 +24,6 @@ import withRegistryProvider from './with-registry-provider'; import { mediaUpload } from '../../utils'; import ReusableBlocksButtons from '../reusable-blocks-buttons'; import ConvertToGroupButtons from '../convert-to-group-buttons'; -import InserterMenuDownloadableBlocksPanel from '../inserter-menu-downloadable-blocks-panel'; const fetchLinkSuggestions = async ( search ) => { const posts = await apiFetch( { @@ -40,11 +38,10 @@ const fetchLinkSuggestions = async ( search ) => { id: post.id, url: post.url, title: decodeEntities( post.title ) || __( '(no title)' ), + type: post.subtype || post.type, } ) ); }; -const UNINSTALL_ERROR_NOTICE_ID = 'block-uninstall-error'; - class EditorProvider extends Component { constructor( props ) { super( ...arguments ); @@ -109,10 +106,11 @@ class EditorProvider extends Component { '__experimentalEnableLegacyWidgetBlock', '__experimentalEnableMenuBlock', '__experimentalBlockDirectory', + '__experimentalEnableFullSiteEditing', 'showInserterHelpPanel', ] ), + mediaUpload: hasUploadPermissions ? mediaUpload : undefined, __experimentalReusableBlocks: reusableBlocks, - __experimentalMediaUpload: hasUploadPermissions ? mediaUpload : undefined, __experimentalFetchLinkSuggestions: fetchLinkSuggestions, __experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML, }; @@ -140,21 +138,6 @@ class EditorProvider extends Component { if ( this.props.settings !== prevProps.settings ) { this.props.updateEditorSettings( this.props.settings ); } - - // When a block is installed from the inserter and is unused, - // it is removed when saving the post. - // Todo: move this to the edit-post package into a separate component. - if ( ! isEqual( this.props.downloadableBlocksToUninstall, prevProps.downloadableBlocksToUninstall ) ) { - this.props.downloadableBlocksToUninstall.forEach( ( blockType ) => { - this.props.uninstallBlock( blockType, noop, () => { - this.props.createWarningNotice( - __( 'Block previews can\'t uninstall.' ), { - id: UNINSTALL_ERROR_NOTICE_ID, - } ); - } ); - unregisterBlockType( blockType.name ); - } ); - } } componentWillUnmount() { @@ -187,19 +170,20 @@ class EditorProvider extends Component { ); return ( - <EntityProvider kind="postType" type={ post.type } id={ post.id }> - <BlockEditorProvider - value={ blocks } - onInput={ resetEditorBlocksWithoutUndoLevel } - onChange={ resetEditorBlocks } - settings={ editorSettings } - useSubRegistry={ false } - > - { children } - <ReusableBlocksButtons /> - <ConvertToGroupButtons /> - { editorSettings.__experimentalBlockDirectory && <InserterMenuDownloadableBlocksPanel /> } - </BlockEditorProvider> + <EntityProvider kind="root" type="site"> + <EntityProvider kind="postType" type={ post.type } id={ post.id }> + <BlockEditorProvider + value={ blocks } + onInput={ resetEditorBlocksWithoutUndoLevel } + onChange={ resetEditorBlocks } + settings={ editorSettings } + useSubRegistry={ false } + > + { children } + <ReusableBlocksButtons /> + <ConvertToGroupButtons /> + </BlockEditorProvider> + </EntityProvider> </EntityProvider> ); } @@ -215,10 +199,6 @@ export default compose( [ __experimentalGetReusableBlocks, } = select( 'core/editor' ); const { canUser } = select( 'core' ); - const { getInstalledBlockTypes } = select( 'core/block-directory' ); - const { getBlocks } = select( 'core/block-editor' ); - - const downloadableBlocksToUninstall = differenceBy( getInstalledBlockTypes(), getBlocks(), 'name' ); return { canUserUseUnfilteredHTML: canUserUseUnfilteredHTML(), @@ -226,7 +206,6 @@ export default compose( [ blocks: getEditorBlocks(), reusableBlocks: __experimentalGetReusableBlocks(), hasUploadPermissions: defaultTo( canUser( 'create', 'media' ), true ), - downloadableBlocksToUninstall, }; } ), withDispatch( ( dispatch ) => { @@ -238,7 +217,6 @@ export default compose( [ __experimentalTearDownEditor, } = dispatch( 'core/editor' ); const { createWarningNotice } = dispatch( 'core/notices' ); - const { uninstallBlock } = dispatch( 'core/block-directory' ); return { setupEditor, @@ -252,7 +230,6 @@ export default compose( [ } ); }, tearDownEditor: __experimentalTearDownEditor, - uninstallBlock, }; } ), ] )( EditorProvider ); diff --git a/packages/editor/src/editor-styles.scss b/packages/editor/src/editor-styles.scss index c44e3d87a32299..978b34a61deaf3 100644 --- a/packages/editor/src/editor-styles.scss +++ b/packages/editor/src/editor-styles.scss @@ -104,6 +104,8 @@ ul, ol { margin-bottom: $default-block-margin; padding: inherit; + padding-left: 1.3em; + margin-left: 1.3em; // Remove bottom margin from nested lists. ul, diff --git a/packages/editor/src/store/actions.native.js b/packages/editor/src/store/actions.native.js index 17733a0e47b408..0154c92324640e 100644 --- a/packages/editor/src/store/actions.native.js +++ b/packages/editor/src/store/actions.native.js @@ -21,9 +21,7 @@ export function togglePostTitleSelection( isSelected = true ) { /** * Action generator used in signalling that the post should autosave. - * - * @param {Object?} options Extra flags to identify the autosave. */ -export function* autosave( ) { +export function* autosave() { RNReactNativeGutenbergBridge.editorDidAutosave(); } diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index 7014d8a3b1fe56..0bf0ddf3f1bb38 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -210,8 +210,8 @@ export function postLock( state = { isLocked: false }, action ) { * * When post saving is locked, the post cannot be published or updated. * - * @param {PostSavingLockState} state Current state. - * @param {Object} action Dispatched action. + * @param {PostLockState} state Current state. + * @param {Object} action Dispatched action. * * @return {PostLockState} Updated state. */ @@ -231,8 +231,8 @@ export function postSavingLock( state = {}, action ) { * * When post autosaving is locked, the post will not autosave. * - * @param {PostAutosavingLockState} state Current state. - * @param {Object} action Dispatched action. + * @param {PostLockState} state Current state. + * @param {Object} action Dispatched action. * * @return {PostLockState} Updated state. */ diff --git a/packages/element/README.md b/packages/element/README.md index 0b96812eacb539..fb0c8089fae2b1 100755 --- a/packages/element/README.md +++ b/packages/element/README.md @@ -148,8 +148,8 @@ _Related_ _Parameters_ -- _component_ `Component`: Component -- _target_ `Element`: DOM node into which element should be rendered +- _child_ `WPElement`: Any renderable child, such as an element, string, or fragment. +- _container_ `HTMLElement`: DOM node into which element should be rendered. <a name="createRef" href="#createRef">#</a> **createRef** @@ -163,12 +163,11 @@ _Returns_ <a name="findDOMNode" href="#findDOMNode">#</a> **findDOMNode** -Finds the dom node of a React component +Finds the dom node of a React component. _Parameters_ -- _component_ `Component`: component's instance -- _target_ `Element`: DOM node into which element should be rendered +- _component_ `WPComponent`: Component's instance. <a name="forwardRef" href="#forwardRef">#</a> **forwardRef** @@ -203,7 +202,7 @@ _Returns_ <a name="isValidElement" href="#isValidElement">#</a> **isValidElement** -Checks if an object is a valid WPElement +Checks if an object is a valid WPElement. _Parameters_ @@ -225,6 +224,30 @@ _Related_ - <https://reactjs.org/docs/react-api.html#reactmemo> +<a name="Platform" href="#Platform">#</a> **Platform** + +Component used to detect the current Platform being used. +Use Platform.OS === 'web' to detect if running on web enviroment. + +This is the same concept as the React Native implementation. + +_Related_ + +- <https://facebook.github.io/react-native/docs/platform-specific-code#platform-module> + +Here is an example of how to use the select method: + +_Usage_ + +```js +import { Platform } from '@wordpress/element'; + +const placeholderLabel = Platform.select( { + native: __( 'Add media' ), + web: __( 'Drag images, upload new ones or select files from your library.' ), +} ); +``` + <a name="RawHTML" href="#RawHTML">#</a> **RawHTML** Component used as equivalent of Fragment with unescaped HTML, in cases where @@ -240,7 +263,7 @@ _Parameters_ _Returns_ -- `WPElement`: Dangerously-rendering element. +- `WPComponent`: Dangerously-rendering component. <a name="render" href="#render">#</a> **render** @@ -248,8 +271,8 @@ Renders a given element into the target DOM node. _Parameters_ -- _element_ `WPElement`: Element to render -- _target_ `Element`: DOM node into which element should be rendered +- _element_ `WPElement`: Element to render. +- _target_ `HTMLElement`: DOM node into which element should be rendered. <a name="renderToString" href="#renderToString">#</a> **renderToString** diff --git a/packages/element/package.json b/packages/element/package.json index 187e80d149b4a5..c7e4ac61ff491c 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/element", - "version": "2.8.0", + "version": "2.8.2", "description": "Element React module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/element/src/index.js b/packages/element/src/index.js index d9d4bf87ecf05d..ad8bae6eff98b0 100644 --- a/packages/element/src/index.js +++ b/packages/element/src/index.js @@ -1,5 +1,6 @@ export * from './react'; export * from './react-platform'; export * from './utils'; +export { default as Platform } from './platform'; export { default as renderToString } from './serialize'; export { default as RawHTML } from './raw-html'; diff --git a/packages/element/src/platform.android.js b/packages/element/src/platform.android.js new file mode 100644 index 00000000000000..4f0da0cab9c683 --- /dev/null +++ b/packages/element/src/platform.android.js @@ -0,0 +1,18 @@ +/** + * External dependencies + */ +import { Platform as OriginalPlatform } from 'react-native'; + +const Platform = { + ...OriginalPlatform, + select: ( spec ) => { + if ( 'android' in spec ) { + return spec.android; + } else if ( 'native' in spec ) { + return spec.native; + } + return spec.default; + }, +}; + +export default Platform; diff --git a/packages/element/src/platform.ios.js b/packages/element/src/platform.ios.js new file mode 100644 index 00000000000000..bbadff1f193319 --- /dev/null +++ b/packages/element/src/platform.ios.js @@ -0,0 +1,18 @@ +/** + * External dependencies + */ +import { Platform as OriginalPlatform } from 'react-native'; + +const Platform = { + ...OriginalPlatform, + select: ( spec ) => { + if ( 'ios' in spec ) { + return spec.ios; + } else if ( 'native' in spec ) { + return spec.native; + } + return spec.default; + }, +}; + +export default Platform; diff --git a/packages/element/src/platform.js b/packages/element/src/platform.js new file mode 100644 index 00000000000000..328f5523b6f95f --- /dev/null +++ b/packages/element/src/platform.js @@ -0,0 +1,32 @@ +/** + * Parts of this source were derived and modified from react-native-web, + * released under the MIT license. + * + * Copyright (c) 2016-present, Nicolas Gallagher. + * Copyright (c) 2015-present, Facebook, Inc. + * + */ +const Platform = { + OS: 'web', + select: ( spec ) => ( 'web' in spec ? spec.web : spec.default ), +}; +/** + * Component used to detect the current Platform being used. + * Use Platform.OS === 'web' to detect if running on web enviroment. + * + * This is the same concept as the React Native implementation. + * + * @see https://facebook.github.io/react-native/docs/platform-specific-code#platform-module + * + * Here is an example of how to use the select method: + * @example + * ```js + * import { Platform } from '@wordpress/element'; + * + * const placeholderLabel = Platform.select( { + * native: __( 'Add media' ), + * web: __( 'Drag images, upload new ones or select files from your library.' ), + * } ); + * ``` + */ +export default Platform; diff --git a/packages/element/src/raw-html.js b/packages/element/src/raw-html.js index 2fa3618c417d88..0f56060e3057b6 100644 --- a/packages/element/src/raw-html.js +++ b/packages/element/src/raw-html.js @@ -13,7 +13,7 @@ import { createElement } from './react'; * @param {string} props.children HTML to render. * @param {Object} props.props Any additonal props to be set on the containing div. * - * @return {WPElement} Dangerously-rendering element. + * @return {WPComponent} Dangerously-rendering component. */ export default function RawHTML( { children, ...props } ) { // The DIV wrapper will be stripped by serializer, unless there are diff --git a/packages/element/src/react-platform.js b/packages/element/src/react-platform.js index e4ca37da7e11bc..44a3422ed79773 100644 --- a/packages/element/src/react-platform.js +++ b/packages/element/src/react-platform.js @@ -13,24 +13,24 @@ import { * * @see https://github.com/facebook/react/issues/10309#issuecomment-318433235 * - * @param {Component} component Component - * @param {Element} target DOM node into which element should be rendered + * @param {WPElement} child Any renderable child, such as an element, + * string, or fragment. + * @param {HTMLElement} container DOM node into which element should be rendered. */ export { createPortal }; /** - * Finds the dom node of a React component + * Finds the dom node of a React component. * - * @param {Component} component component's instance - * @param {Element} target DOM node into which element should be rendered + * @param {WPComponent} component Component's instance. */ export { findDOMNode }; /** * Renders a given element into the target DOM node. * - * @param {WPElement} element Element to render - * @param {Element} target DOM node into which element should be rendered + * @param {WPElement} element Element to render. + * @param {HTMLElement} target DOM node into which element should be rendered. */ export { render }; diff --git a/packages/element/src/react.js b/packages/element/src/react.js index 561776257f388e..2776f514b329e5 100644 --- a/packages/element/src/react.js +++ b/packages/element/src/react.js @@ -28,6 +28,24 @@ import { } from 'react'; import { isString } from 'lodash'; +/** + * Object containing a React element. + * + * @typedef {react.ReactElement} WPElement + */ + +/** + * Object containing a React component. + * + * @typedef {react.Component} WPComponent + */ + +/** + * Object containing a React synthetic event. + * + * @typedef {react.SyntheticEvent} WPSyntheticEvent + */ + /** * Object that provides utilities for dealing with React children. */ @@ -63,8 +81,8 @@ export { createContext }; * * @param {?(string|Function)} type Tag name or element creator * @param {Object} props Element properties, either attribute - * set to apply to DOM node or values to - * pass through to element creator + * set to apply to DOM node or values to + * pass through to element creator * @param {...WPElement} children Descendant elements * * @return {WPElement} Element. @@ -99,7 +117,7 @@ export { forwardRef }; export { Fragment }; /** - * Checks if an object is a valid WPElement + * Checks if an object is a valid WPElement. * * @param {Object} objectToCheck The object to be checked. * @@ -185,7 +203,7 @@ export { Suspense }; * @return {Array} The concatenated value. */ export function concatChildren( ...childrenArguments ) { - return childrenArguments.reduce( ( result, children, i ) => { + return childrenArguments.reduce( ( accumulator, children, i ) => { Children.forEach( children, ( child, j ) => { if ( child && 'string' !== typeof child ) { child = cloneElement( child, { @@ -193,10 +211,10 @@ export function concatChildren( ...childrenArguments ) { } ); } - result.push( child ); + accumulator.push( child ); } ); - return result; + return accumulator; }, [] ); } diff --git a/packages/element/src/test/platform.js b/packages/element/src/test/platform.js new file mode 100644 index 00000000000000..fc98f4e19b22a9 --- /dev/null +++ b/packages/element/src/test/platform.js @@ -0,0 +1,19 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; +/** + * Internal dependencies + */ +import Platform from '../platform'; + +describe( 'Platform', () => { + it( 'is chooses the right thing', () => { + const element = Platform.select( { + web: shallow( <div></div> ), + native: shallow( <button></button> ), + } ); + + expect( element.type() ).toBe( 'div' ); + } ); +} ); diff --git a/packages/element/src/test/platform.native.js b/packages/element/src/test/platform.native.js new file mode 100644 index 00000000000000..da6717d66409e0 --- /dev/null +++ b/packages/element/src/test/platform.native.js @@ -0,0 +1,19 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; +/** + * Internal dependencies + */ +import Platform from '../platform'; + +describe( 'Platform', () => { + it( 'is chooses the right thing', () => { + const element = Platform.select( { + web: shallow( <div></div> ), + native: shallow( <button></button> ), + } ); + + expect( element.type() ).toBe( 'button' ); + } ); +} ); diff --git a/packages/env/.npmrc b/packages/env/.npmrc new file mode 100644 index 00000000000000..43c97e719a5a82 --- /dev/null +++ b/packages/env/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/env/README.md b/packages/env/README.md index 45736ee1fa29bb..1f06dabf9ce710 100644 --- a/packages/env/README.md +++ b/packages/env/README.md @@ -7,7 +7,7 @@ ```sh $ npm -g i @wordpress/env -$ cd path/to/gutenberg # WordPress install will be in path/to/gutenberg-wordpress. +$ cd path/to/plugin-or-theme # WordPress install will be in path/to/plugin-or-theme-wordpress. $ wp-env --help @@ -15,8 +15,10 @@ wp-env <command> Commands: wp-env start [ref] Starts WordPress for development on port 8888 - (​http://localhost:8888​) and tests on port 8889 - (​http://localhost:8889​). If the current working + (​http://localhost:8888​) (override with + WP_ENV_PORT) and tests on port 8889 + (​http://localhost:8889​) (override with + WP_ENV_TESTS_PORT). If the current working directory is a plugin and/or has e2e-tests with plugins and/or mu-plugins, they will be mounted appropiately. @@ -34,10 +36,11 @@ Options: ```sh wp-env start [ref] -Starts WordPress for development on port 8888 (​http://localhost:8888​) and -tests on port 8889 (​http://localhost:8889​). If the current working directory -is a plugin and/or has e2e-tests with plugins and/or mu-plugins, they will be -mounted appropiately. +Starts WordPress for development on port 8888 (​http://localhost:8888​) +(override with WP_ENV_PORT) and tests on port 8889 (​http://localhost:8889​) +(override with WP_ENV_TESTS_PORT). If the current working directory is a plugin +and/or has e2e-tests with plugins and/or mu-plugins, they will be mounted +appropiately. Positionals: ref A `https://github.com/WordPress/WordPress` git repo branch or commit for @@ -63,3 +66,5 @@ Positionals: environment Which environments' databases to clean. [string] [choices: "all", "development", "tests"] [default: "tests"] ``` + +<br/><br/><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p> diff --git a/packages/env/lib/cli.js b/packages/env/lib/cli.js index 6f134afd9ae421..98dc1ecb42ce26 100644 --- a/packages/env/lib/cli.js +++ b/packages/env/lib/cli.js @@ -46,10 +46,10 @@ module.exports = function cli() { chalk`Starts WordPress for development on port {bold.underline ${ terminalLink( '8888', 'http://localhost:8888' - ) }} and tests on port {bold.underline ${ terminalLink( + ) }} (override with WP_ENV_PORT) and tests on port {bold.underline ${ terminalLink( '8889', 'http://localhost:8889' - ) }}. If the current working directory is a plugin and/or has e2e-tests with plugins and/or mu-plugins, they will be mounted appropiately.` + ) }} (override with WP_ENV_TESTS_PORT). If the current working directory is a plugin and/or has e2e-tests with plugins and/or mu-plugins, they will be mounted appropiately.` ), ( args ) => { args.positional( 'ref', { diff --git a/packages/env/lib/create-docker-compose-config.js b/packages/env/lib/create-docker-compose-config.js index 7a3dfc7d33b6aa..f5971723148b57 100644 --- a/packages/env/lib/create-docker-compose-config.js +++ b/packages/env/lib/create-docker-compose-config.js @@ -1,17 +1,18 @@ module.exports = function createDockerComposeConfig( - pluginPath, - pluginName, - pluginTestsPath + cwd, + cwdName, + cwdTestsPath, + context ) { const commonVolumes = ` - - ${ pluginPath }/:/var/www/html/wp-content/plugins/${ pluginName }/ - - ${ pluginPath }${ pluginTestsPath }/e2e-tests/mu-plugins/:/var/www/html/wp-content/mu-plugins/ - - ${ pluginPath }${ pluginTestsPath }/e2e-tests/plugins/:/var/www/html/wp-content/plugins/${ pluginName }-test-plugins/`; + - ${ cwd }/:/var/www/html/wp-content/${ context.type }s/${ cwdName }/ + - ${ cwd }${ cwdTestsPath }/e2e-tests/mu-plugins/:/var/www/html/wp-content/mu-plugins/ + - ${ cwd }${ cwdTestsPath }/e2e-tests/plugins/:/var/www/html/wp-content/plugins/${ cwdName }-test-plugins/`; const volumes = ` - - ${ pluginPath }/../${ pluginName }-wordpress/:/var/www/html/${ commonVolumes }`; + - ${ cwd }/../${ cwdName }-wordpress/:/var/www/html/${ commonVolumes }`; const testsVolumes = ` - tests-wordpress:/var/www/html/${ commonVolumes }`; - return `version: '2' + return `version: '2.1' volumes: tests-wordpress: services: @@ -28,7 +29,7 @@ services: WORDPRESS_DB_PASSWORD: password image: wordpress ports: - - 8888:80 + - \${WP_ENV_PORT:-8888}:80 volumes:${ volumes } wordpress-cli: depends_on: @@ -44,7 +45,7 @@ services: WORDPRESS_DB_PASSWORD: password image: wordpress ports: - - 8889:80 + - \${WP_ENV_TESTS_PORT:-8889}:80 volumes:${ testsVolumes } tests-wordpress-cli: depends_on: diff --git a/packages/env/lib/detect-context.js b/packages/env/lib/detect-context.js new file mode 100644 index 00000000000000..afc23f6dd5f6f3 --- /dev/null +++ b/packages/env/lib/detect-context.js @@ -0,0 +1,45 @@ +'use strict'; +/** + * External dependencies + */ +const util = require( 'util' ); +const fs = require( 'fs' ); +const stream = require( 'stream' ); +const path = require( 'path' ); + +/** + * Promisified dependencies + */ +const readDir = util.promisify( fs.readdir ); +const finished = util.promisify( stream.finished ); + +module.exports = async function detectContext() { + const context = {}; + + // Race multiple file read streams against each other until + // a plugin or theme header is found. + const files = ( await readDir( './' ) ).filter( + ( file ) => path.extname( file ) === '.php' || path.basename( file ) === 'style.css' + ); + const streams = []; + for ( const file of files ) { + const fileStream = fs.createReadStream( file, 'utf8' ); + fileStream.on( 'data', ( text ) => { + const [ , type ] = text.match( /(Plugin|Theme) Name: .*[\r\n]/ ) || []; + if ( type ) { + context.type = type.toLowerCase(); + + // Stop the creation of new streams by mutating the iterated array. We can't `break`, because we are inside a function. + files.splice( 0 ); + fileStream.destroy(); + streams.forEach( ( otherFileStream ) => otherFileStream.destroy() ); + } + } ); + streams.push( fileStream ); + } + await Promise.all( + streams.map( ( fileStream ) => finished( fileStream ).catch( () => {} ) ) + ); + + return context; +}; diff --git a/packages/env/lib/env.js b/packages/env/lib/env.js index 159bb2ac2fc45f..c2ba83ac24c221 100644 --- a/packages/env/lib/env.js +++ b/packages/env/lib/env.js @@ -11,12 +11,13 @@ const wait = require( 'util' ).promisify( setTimeout ); /** * Internal dependencies */ +const detectContext = require( './detect-context' ); const createDockerComposeConfig = require( './create-docker-compose-config' ); // Config Variables -const pluginPath = process.cwd(); -const pluginName = path.basename( pluginPath ); -const pluginTestsPath = fs.existsSync( './packages' ) ? '/packages' : ''; +const cwd = process.cwd(); +const cwdName = path.basename( cwd ); +const cwdTestsPath = fs.existsSync( './packages' ) ? '/packages' : ''; const dockerComposeOptions = { config: path.join( __dirname, 'docker-compose.yml' ), }; @@ -30,18 +31,22 @@ const wpCliRun = ( command, isTests = false ) => ); const setupSite = ( isTests = false ) => wpCliRun( - `wp core install --url=localhost:888${ - isTests ? '9' : '8' - } --title=Gutenberg --admin_user=admin --admin_password=password --admin_email=admin@wordpress.org`, + `wp core install --url=localhost:${ + isTests ? + process.env.WP_ENV_TESTS_PORT || 8889 : + process.env.WP_ENV_PORT || 8888 + } --title=${ cwdName } --admin_user=admin --admin_password=password --admin_email=admin@wordpress.org`, isTests ); -const activatePlugin = ( isTests = false ) => - wpCliRun( `wp plugin activate ${ pluginName }`, isTests ); +const activateContext = ( context, isTests = false ) => + wpCliRun( `wp ${ context.type } activate ${ cwdName }`, isTests ); const resetDatabase = ( isTests = false ) => wpCliRun( 'wp db reset --yes', isTests ); module.exports = { async start( { ref, spinner = {} } ) { + const context = await detectContext(); + spinner.text = `Downloading WordPress@${ ref } 0/100%.`; const gitFetchOptions = { fetchOpts: { @@ -62,7 +67,7 @@ module.exports = { }; // Clone or get the repo. - const repoPath = `../${ pluginName }-wordpress/`; + const repoPath = `../${ cwdName }-wordpress/`; const repo = await NodeGit.Clone( 'https://github.com/WordPress/WordPress.git', repoPath, @@ -92,10 +97,10 @@ module.exports = { } spinner.text = `Downloading WordPress@${ ref } 100/100%.`; - spinner.text = `Installing WordPress@${ ref }.`; + spinner.text = `Starting WordPress@${ ref }.`; fs.writeFileSync( dockerComposeOptions.config, - createDockerComposeConfig( pluginPath, pluginName, pluginTestsPath ) + createDockerComposeConfig( cwd, cwdName, cwdTestsPath, context ) ); // These will bring up the database container, @@ -119,11 +124,14 @@ module.exports = { .catch( retryableSiteSetup ) .catch( retryableSiteSetup ); - await Promise.all( [ activatePlugin(), activatePlugin( true ) ] ); + await Promise.all( [ + activateContext( context ), + activateContext( context, true ), + ] ); // Remove dangling containers and finish. await dockerCompose.rm( dockerComposeOptions ); - spinner.text = `Installed WordPress@${ ref }.`; + spinner.text = `Started WordPress@${ ref }.`; }, async stop( { spinner = {} } ) { @@ -133,6 +141,8 @@ module.exports = { }, async clean( { environment, spinner } ) { + const context = await detectContext(); + const description = `${ environment } environment${ environment === 'all' ? 's' : '' }`; @@ -144,7 +154,7 @@ module.exports = { tasks.push( resetDatabase() .then( setupSite ) - .then( activatePlugin ) + .then( activateContext.bind( null, context ) ) .catch( () => {} ) ); } @@ -152,7 +162,7 @@ module.exports = { tasks.push( resetDatabase( true ) .then( setupSite.bind( null, true ) ) - .then( activatePlugin.bind( null, true ) ) + .then( activateContext.bind( null, context, true ) ) .catch( () => {} ) ); } diff --git a/packages/env/package.json b/packages/env/package.json index 6be0539b3725cf..19f0fe52cdc047 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "1.0.0", + "version": "0.0.0", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/env/tests/cli.test.js b/packages/env/test/cli.js similarity index 100% rename from packages/env/tests/cli.test.js rename to packages/env/test/cli.js diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index b93e62d2a76e42..19d4e1d0b895c5 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/escape-html", - "version": "1.5.0", + "version": "1.5.1", "description": "Escape HTML utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/eslint-plugin/configs/jsdoc.js b/packages/eslint-plugin/configs/jsdoc.js index 614816cb734a21..8e0935dc316881 100644 --- a/packages/eslint-plugin/configs/jsdoc.js +++ b/packages/eslint-plugin/configs/jsdoc.js @@ -1,5 +1,55 @@ +/** + * External dependencies + */ const globals = require( 'globals' ); +/** + * The temporary list of types defined in Gutenberg which are whitelisted to avoid + * ESLint warnings. It should be removed once importing is going to be implemented + * in the tool which generates public APIs from JSDoc comments. Related issue to + * fix the root cause `@wordpress/docgen`: + * https://github.com/WordPress/gutenberg/issues/18045. + */ +const temporaryWordPressInternalTypes = [ + 'WPBlockChildren', + 'WPBlockNode', + 'WPBlockSelection', + 'WPBlockSerializationOptions', + 'WPBlock', + 'WPBlockTypeIcon', + 'WPBlockTypeIconRender', + 'WPBlockTypeIconDescriptor', + 'WPDataPersistencePluginOptions', + 'WPDataPlugin', + 'WPDataRegistry', + 'WPComponent', + 'WPCompleter', + 'WPElement', + 'WPFormat', + 'WPEditorInserterItem', + 'WPNotice', + 'WPNoticeAction', + 'WPPlugin', + 'WPShortcode', + 'WPShortcodeAttrs', + 'WPShortcodeMatch', + 'WPSyntheticEvent', +]; + +/** + * The temporary list of external types used in Gutenberg which are whitelisted + * to avoid ESLint warnings. It's similar to `wordpressInternalTypes` and it + * should be removed once the related issues is fixed: + * https://github.com/WordPress/gutenberg/issues/18045 + */ +const temporaryExternalTypes = [ + 'DOMHighResTimeStamp', + 'espree', + 'moment', + 'puppeteer', + 'react', +]; + /** * Helpful utilities that are globally defined and known to the TypeScript compiler. * @@ -50,6 +100,8 @@ module.exports = { // generally refer to window-level event listeners and are not a valid type to reference (e.g. `onclick`). ...Object.keys( globals.browser ).filter( ( k ) => /^[A-Z]/.test( k ) ), ...typescriptUtilityTypes, + ...temporaryWordPressInternalTypes, + ...temporaryExternalTypes, 'void', ], } ], diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 50605fb08b7508..e7385c46bd2bfc 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "1.9.0", + "version": "1.9.3", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/src/image/style.scss b/packages/format-library/src/image/style.scss index 93bfc96ae7a166..5209377f6448e4 100644 --- a/packages/format-library/src/image/style.scss +++ b/packages/format-library/src/image/style.scss @@ -2,8 +2,11 @@ display: flex; .components-icon-button { - height: $icon-button-size + $grid-size + $grid-size; align-self: flex-end; + height: $grid-size * 4 - ($border-width * 2); + margin-bottom: $grid-size; + margin-right: $grid-size; + padding: 0 6px; } } @@ -15,7 +18,13 @@ min-width: 150px; max-width: 500px; - &.components-base-control .components-base-control__field { - margin-bottom: 0; + &.components-base-control { + .components-base-control__field { + margin-bottom: 0; + } + + .components-base-control__label { + display: block; + } } } diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 3fabae7a1d053d..a164a7cdbb44f8 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/is-shallow-equal", - "version": "1.6.0", + "version": "1.6.1", "description": "Test for shallow equality between two objects or arrays.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-puppeteer-axe/src/index.js b/packages/jest-puppeteer-axe/src/index.js index 336cec9bcf8161..7bba05d8965d28 100644 --- a/packages/jest-puppeteer-axe/src/index.js +++ b/packages/jest-puppeteer-axe/src/index.js @@ -53,13 +53,13 @@ function formatViolations( violations ) { * * @see https://github.com/dequelabs/axe-puppeteer * - * @param {Page} page Puppeteer's page instance. - * @param {?Object} params Optional Axe API options. - * @param {?string|Array} params.include CSS selector(s) to add to the list of elements - * to include in analysis. - * @param {?string|Array} params.exclude CSS selector(s) to add to the list of elements - * to exclude from analysis. - * @param {?Array} params.disabledRules The list of Axe rules to skip from verification. + * @param {puppeteer.Page} page Puppeteer's page instance. + * @param {?Object} params Optional Axe API options. + * @param {?string|Array} params.include CSS selector(s) to add to the list of elements + * to include in analysis. + * @param {?string|Array} params.exclude CSS selector(s) to add to the list of elements + * to exclude from analysis. + * @param {?Array} params.disabledRules The list of Axe rules to skip from verification. * * @return {Object} A matcher object with two keys `pass` and `message`. */ diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index ba8a5cbe103060..a62da6c45f89ba 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keycodes", - "version": "2.6.0", + "version": "2.6.2", "description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 6df9f6a7b996a0..54c7773292205d 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/list-reusable-blocks", - "version": "1.8.0", + "version": "1.8.3", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index 90ecc22a60a0c6..7d6af3e41f63ba 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/media-utils", - "version": "1.2.0", + "version": "1.2.3", "description": "WordPress Media Upload Utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/media-utils/src/components/media-upload/index.js b/packages/media-utils/src/components/media-upload/index.js index 0ba3db65fd43b1..eac379f9ebb68d 100644 --- a/packages/media-utils/src/components/media-upload/index.js +++ b/packages/media-utils/src/components/media-upload/index.js @@ -125,9 +125,6 @@ class MediaUpload extends Component { } else { const frameConfig = { title, - button: { - text: __( 'Select' ), - }, multiple, }; if ( !! allowedTypes ) { diff --git a/packages/notices/package.json b/packages/notices/package.json index ed582e8ca7dc50..6840e145531d50 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "1.8.0", + "version": "1.8.2", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/notices/src/store/actions.js b/packages/notices/src/store/actions.js index f1771b6ca8ae2f..0fed89a60e0f31 100644 --- a/packages/notices/src/store/actions.js +++ b/packages/notices/src/store/actions.js @@ -8,6 +8,17 @@ import { uniqueId } from 'lodash'; */ import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants'; +/** + * @typedef {Object} WPNoticeAction Object describing a user action option associated with a notice. + * + * @property {string} label Message to use as action label. + * @property {?string} url Optional URL of resource if action incurs + * browser navigation. + * @property {?Function} onClick Optional function to invoke when action is + * triggered by user. + * + */ + /** * Yields action objects used in signalling that a notice is to be created. * diff --git a/packages/notices/src/store/selectors.js b/packages/notices/src/store/selectors.js index 3a0bc75ad0f02a..358a159aa8dc66 100644 --- a/packages/notices/src/store/selectors.js +++ b/packages/notices/src/store/selectors.js @@ -38,17 +38,6 @@ const DEFAULT_NOTICES = []; * */ -/** - * @typedef {Object} WPNoticeAction Object describing a user action option associated with a notice. - * - * @property {string} label Message to use as action label. - * @property {?string} url Optional URL of resource if action incurs - * browser navigation. - * @property {?Function} onClick Optional function to invoke when action is - * triggered by user. - * - */ - /** * Returns all notices as an array, optionally for a given context. Defaults to * the global context. diff --git a/packages/nux/package.json b/packages/nux/package.json index 858ddc289df131..913a16112bc36a 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/nux", - "version": "3.7.0", + "version": "3.7.2", "description": "NUX (New User eXperience) module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/nux/src/store/selectors.js b/packages/nux/src/store/selectors.js index 9225bd97077eee..58decf89455191 100644 --- a/packages/nux/src/store/selectors.js +++ b/packages/nux/src/store/selectors.js @@ -7,7 +7,7 @@ import { includes, difference, keys, has } from 'lodash'; /** * An object containing information about a guide. * - * @typedef {Object} NUX.GuideInfo + * @typedef {Object} NUXGuideInfo * @property {string[]} tipIds Which tips the guide contains. * @property {?string} currentTipId The guide's currently showing tip. * @property {?string} nextTipId The guide's next tip to show. @@ -20,7 +20,7 @@ import { includes, difference, keys, has } from 'lodash'; * @param {Object} state Global application state. * @param {string} tipId The tip to query. * - * @return {?NUX.GuideInfo} Information about the associated guide. + * @return {?NUXGuideInfo} Information about the associated guide. */ export const getAssociatedGuide = createSelector( ( state, tipId ) => { diff --git a/packages/plugins/README.md b/packages/plugins/README.md index 2875c76b0fa8da..9bf049e1bc6abc 100644 --- a/packages/plugins/README.md +++ b/packages/plugins/README.md @@ -26,7 +26,7 @@ _Parameters_ _Returns_ -- `?Object`: Plugin setting. +- `?WPPlugin`: Plugin setting. <a name="getPlugins" href="#getPlugins">#</a> **getPlugins** @@ -34,7 +34,7 @@ Returns all registered plugins. _Returns_ -- `Array`: Plugin settings. +- `Array<WPPlugin>`: Plugin settings. <a name="PluginArea" href="#PluginArea">#</a> **PluginArea** @@ -71,7 +71,7 @@ const Layout = () => ( _Returns_ -- `WPElement`: Plugin area. +- `WPComponent`: The component to be rendered. <a name="registerPlugin" href="#registerPlugin">#</a> **registerPlugin** @@ -143,14 +143,12 @@ registerPlugin( 'plugin-name', { _Parameters_ -- _name_ `string`: A string identifying the plugin. Must be unique across all registered plugins. -- _settings_ `Object`: The settings for this plugin. -- _settings.icon_ `(string|WPElement|Function)`: An icon to be shown in the UI. It can be a slug of the Dashicon, or an element (or function returning an element) if you choose to render your own SVG. -- _settings.render_ `Function`: A component containing the UI elements to be rendered. +- _name_ `string`: A string identifying the plugin.Must be unique across all registered plugins. +- _settings_ `WPPlugin`: The settings for this plugin. _Returns_ -- `Object`: The final plugin settings object. +- `WPPlugin`: The final plugin settings object. <a name="unregisterPlugin" href="#unregisterPlugin">#</a> **unregisterPlugin** @@ -191,7 +189,7 @@ _Parameters_ _Returns_ -- `Component`: Enhanced component with injected context as props. +- `WPComponent`: Enhanced component with injected context as props. <!-- END TOKEN(Autogenerated API docs) --> diff --git a/packages/plugins/package.json b/packages/plugins/package.json index f935e31b1b3de5..3c8ca7edfcb63e 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/plugins", - "version": "2.7.0", + "version": "2.7.2", "description": "Plugins module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/plugins/src/api/index.js b/packages/plugins/src/api/index.js index 6e6ea102a80ffd..5e8fe1d19c851d 100644 --- a/packages/plugins/src/api/index.js +++ b/packages/plugins/src/api/index.js @@ -10,6 +10,22 @@ import { applyFilters, doAction } from '@wordpress/hooks'; */ import { isFunction } from 'lodash'; +/** + * Defined behavior of a plugin type. + * + * @typedef {Object} WPPlugin + * + * @property {string} name A string identifying the plugin. Must be + * unique across all registered plugins. + * unique across all registered plugins. + * @property {string|WPElement|Function} icon An icon to be shown in the UI. It can + * be a slug of the Dashicon, or an element + * (or function returning an element) if you + * choose to render your own SVG. + * @property {Function} render A component containing the UI elements + * to be rendered. + */ + /** * Plugin definitions keyed by plugin name. * @@ -20,11 +36,9 @@ const plugins = {}; /** * Registers a plugin to the editor. * - * @param {string} name A string identifying the plugin. Must be unique across all registered plugins. - * @param {Object} settings The settings for this plugin. - * @param {string|WPElement|Function} settings.icon An icon to be shown in the UI. It can be a slug of the Dashicon, - * or an element (or function returning an element) if you choose to render your own SVG. - * @param {Function} settings.render A component containing the UI elements to be rendered. + * @param {string} name A string identifying the plugin.Must be + * unique across all registered plugins. + * @param {WPPlugin} settings The settings for this plugin. * * @example <caption>ES5</caption> * ```js @@ -90,7 +104,7 @@ const plugins = {}; * } ); * ``` * - * @return {Object} The final plugin settings object. + * @return {WPPlugin} The final plugin settings object. */ export function registerPlugin( name, settings ) { if ( typeof settings !== 'object' ) { @@ -181,7 +195,7 @@ export function unregisterPlugin( name ) { * * @param {string} name Plugin name. * - * @return {?Object} Plugin setting. + * @return {?WPPlugin} Plugin setting. */ export function getPlugin( name ) { return plugins[ name ]; @@ -190,7 +204,7 @@ export function getPlugin( name ) { /** * Returns all registered plugins. * - * @return {Array} Plugin settings. + * @return {WPPlugin[]} Plugin settings. */ export function getPlugins() { return Object.values( plugins ); diff --git a/packages/plugins/src/components/plugin-area/index.js b/packages/plugins/src/components/plugin-area/index.js index 1cf6f15b528795..e03e5458d50ef4 100644 --- a/packages/plugins/src/components/plugin-area/index.js +++ b/packages/plugins/src/components/plugin-area/index.js @@ -47,7 +47,7 @@ import { getPlugins } from '../../api'; * ); * ``` * - * @return {WPElement} Plugin area. + * @return {WPComponent} The component to be rendered. */ class PluginArea extends Component { constructor() { diff --git a/packages/plugins/src/components/plugin-context/index.js b/packages/plugins/src/components/plugin-context/index.js index 75fb1b8f1dea01..586891c67eb4ee 100644 --- a/packages/plugins/src/components/plugin-context/index.js +++ b/packages/plugins/src/components/plugin-context/index.js @@ -19,7 +19,7 @@ export { Provider as PluginContextProvider }; * expected to return object of props to * merge with the component's own props. * - * @return {Component} Enhanced component with injected context as props. + * @return {WPComponent} Enhanced component with injected context as props. */ export const withPluginContext = ( mapContextToProps ) => createHigherOrderComponent( ( OriginalComponent ) => { return ( props ) => ( diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index 16f8dbd988281f..de49d37c2fe9c3 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/priority-queue", - "version": "1.3.0", + "version": "1.3.1", "description": "Generic browser priority queue.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index ee2c7d728eac8a..29e53534cf6cd8 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/redux-routine", - "version": "3.6.0", + "version": "3.6.2", "description": "Redux middleware for generator coroutines.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/README.md b/packages/rich-text/README.md index 46a78502ff3ed4..e911c714792064 100644 --- a/packages/rich-text/README.md +++ b/packages/rich-text/README.md @@ -216,11 +216,7 @@ behavior. _Parameters_ - _name_ `string`: Format name. -- _settings_ `Object`: Format settings. -- _settings.tagName_ `string`: The HTML tag this format will wrap the selection with. -- _settings.className_ `[string]`: A class to match the format. -- _settings.title_ `string`: Name of the format. -- _settings.edit_ `Function`: Should return a component for the user to interact with the new registered format. +- _settings_ `WPFormat`: Format settings. _Returns_ diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 544d69294e90e4..4cf3ef016ddaf3 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "3.7.0", + "version": "3.7.2", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js index c82feae869d1e7..c3112d3337fb78 100644 --- a/packages/rich-text/src/component/index.js +++ b/packages/rich-text/src/component/index.js @@ -35,6 +35,7 @@ import { indentListItems } from '../indent-list-items'; import { getActiveFormats } from '../get-active-formats'; import { updateFormats } from '../update-formats'; import { removeLineSeparator } from '../remove-line-separator'; +import { isEmptyLine } from '../is-empty'; /** * Browser dependencies @@ -204,7 +205,7 @@ class RichText extends Component { * * Saves the pasted data as plain text in `pastedPlainText`. * - * @param {PasteEvent} event The paste event. + * @param {ClipboardEvent} event The paste event. */ onPaste( event ) { const { formatTypes, onPaste } = this.props; @@ -258,18 +259,32 @@ class RichText extends Component { } if ( onPaste ) { - // Only process file if no HTML is present. - // Note: a pasted file may have the URL as plain text. - const image = find( [ ...items, ...files ], ( { type } ) => - /^image\/(?:jpe?g|png|gif)$/.test( type ) - ); + files = Array.from( files ); + + Array.from( items ).forEach( ( item ) => { + if ( ! item.getAsFile ) { + return; + } + + const file = item.getAsFile(); + + if ( ! file ) { + return; + } + + const { name, type, size } = file; + + if ( ! find( files, { name, type, size } ) ) { + files.push( file ); + } + } ); onPaste( { value: this.removeEditorOnlyFormats( record ), onChange: this.onChange, html, plainText, - image, + files, } ); } } @@ -337,7 +352,7 @@ class RichText extends Component { /** * Handle input on the next selection change event. * - * @param {SyntheticEvent} event Synthetic input event. + * @param {WPSyntheticEvent} event Synthetic input event. */ onInput( event ) { // For Input Method Editor (IME), used in Chinese, Japanese, and Korean @@ -437,7 +452,7 @@ class RichText extends Component { * native events, `keyup`, `mouseup` and `touchend` synthetic events, and * animation frames after the `focus` event. * - * @param {Event|SyntheticEvent|DOMHighResTimeStamp} event + * @param {Event|WPSyntheticEvent|DOMHighResTimeStamp} event */ onSelectionChange( event ) { if ( @@ -579,7 +594,7 @@ class RichText extends Component { * - delete content if everything is selected, * - trigger the onDelete prop when selection is uncollapsed and at an edge. * - * @param {SyntheticEvent} event A synthetic keyboard event. + * @param {WPSyntheticEvent} event A synthetic keyboard event. */ handleDelete( event ) { const { keyCode } = event; @@ -604,14 +619,6 @@ class RichText extends Component { const { start, end, text } = value; const isReverse = keyCode === BACKSPACE; - if ( multilineTag ) { - const newValue = removeLineSeparator( value, isReverse ); - if ( newValue ) { - this.onChange( newValue ); - event.preventDefault(); - } - } - // Always handle full content deletion ourselves. if ( start === 0 && end !== 0 && end === text.length ) { this.onChange( remove( value ) ); @@ -619,6 +626,23 @@ class RichText extends Component { return; } + if ( multilineTag ) { + let newValue; + + // Check to see if we should remove the first item if empty. + if ( isReverse && value.start === 0 && value.end === 0 && isEmptyLine( value ) ) { + newValue = removeLineSeparator( value, ! isReverse ); + } else { + newValue = removeLineSeparator( value, isReverse ); + } + + if ( newValue ) { + this.onChange( newValue ); + event.preventDefault(); + return; + } + } + // Only process delete if the key press occurs at an uncollapsed edge. if ( ! onDelete || @@ -637,7 +661,7 @@ class RichText extends Component { /** * Triggers the `onEnter` prop on keydown. * - * @param {SyntheticEvent} event A synthetic keyboard event. + * @param {WPSyntheticEvent} event A synthetic keyboard event. */ handleEnter( event ) { if ( event.keyCode !== ENTER ) { @@ -662,7 +686,7 @@ class RichText extends Component { /** * Indents list items on space keydown. * - * @param {SyntheticEvent} event A synthetic keyboard event. + * @param {WPSyntheticEvent} event A synthetic keyboard event. */ handleSpace( event ) { const { keyCode, shiftKey, altKey, metaKey, ctrlKey } = event; @@ -699,7 +723,7 @@ class RichText extends Component { * navigation is handled separately to move correctly around format * boundaries. * - * @param {SyntheticEvent} event A synthetic keyboard event. + * @param {WPSyntheticEvent} event A synthetic keyboard event. */ handleHorizontalNavigation( event ) { const { keyCode, shiftKey, altKey, metaKey, ctrlKey } = event; @@ -805,7 +829,7 @@ class RichText extends Component { * Select object when they are clicked. The browser will not set any * selection when clicking e.g. an image. * - * @param {SyntheticEvent} event Synthetic mousedown or touchstart event. + * @param {WPSyntheticEvent} event Synthetic mousedown or touchstart event. */ onPointerDown( event ) { const { target } = event; diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 09ac451ed56d23..34a1b03fe24ef2 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -30,10 +30,7 @@ import { getActiveFormat } from '../get-active-format'; import { getActiveFormats } from '../get-active-formats'; import { isEmpty, isEmptyLine } from '../is-empty'; import { create } from '../create'; -import { split } from '../split'; import { toHTMLString } from '../to-html-string'; -import { insert } from '../insert'; -import { insertLineSeparator } from '../insert-line-separator'; import { removeLineSeparator } from '../remove-line-separator'; import { isCollapsed } from '../is-collapsed'; import { remove } from '../remove'; @@ -43,28 +40,6 @@ const unescapeSpaces = ( text ) => { return text.replace( /&nbsp;|&#160;/gi, ' ' ); }; -/** - * Calls {@link pasteHandler} with a fallback to plain text when HTML processing - * results in errors - * - * @param {Function} originalPasteHandler The original handler function - * @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 = ( originalPasteHandler, options ) => { - try { - return originalPasteHandler( 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 originalPasteHandler( { ...options, HTML: '' } ); - } -}; - const gutenbergFormatNamesToAztec = { 'core/bold': 'bold', 'core/italic': 'italic', @@ -72,7 +47,7 @@ const gutenbergFormatNamesToAztec = { }; export class RichText extends Component { - constructor( { value, __unstableMultiline: multiline, selectionStart, selectionEnd } ) { + constructor( { value, selectionStart, selectionEnd, __unstableMultilineTag: multiline } ) { super( ...arguments ); this.isMultiline = false; @@ -84,12 +59,12 @@ export class RichText extends Component { if ( this.multilineTag === 'li' ) { this.multilineWrapperTags = [ 'ul', 'ol' ]; } - this.onSplit = this.onSplit.bind( this ); + this.isIOS = Platform.OS === 'ios'; this.createRecord = this.createRecord.bind( this ); this.onChange = this.onChange.bind( this ); - this.onEnter = this.onEnter.bind( this ); - this.onBackspace = this.onBackspace.bind( this ); + this.handleEnter = this.handleEnter.bind( this ); + this.handleDelete = this.handleDelete.bind( this ); this.onPaste = this.onPaste.bind( this ); this.onFocus = this.onFocus.bind( this ); this.onBlur = this.onBlur.bind( this ); @@ -169,63 +144,6 @@ export class RichText extends Component { return { ...value, start, end }; } - /** - * Signals to the RichText owner that the block can be replaced with two - * blocks as a result of splitting the block by pressing enter, or with - * blocks as a result of splitting the block by pasting block content in the - * instance. - * - * @param {Object} record The rich text value to split. - * @param {Array} pastedBlocks The pasted blocks to insert, if any. - */ - onSplit( record, pastedBlocks = [] ) { - const { - __unstableOnReplace: onReplace, - __unstableOnSplit: onSplit, - __unstableOnSplitMiddle: onSplitMiddle, - } = this.props; - - if ( ! onReplace || ! onSplit ) { - return; - } - - const blocks = []; - const [ before, after ] = split( record ); - const hasPastedBlocks = pastedBlocks.length > 0; - - // Create a block with the content before the caret if there's no pasted - // blocks, or if there are pasted blocks and the value is not empty. - // We do not want a leading empty block on paste, but we do if split - // with e.g. the enter key. - if ( ! hasPastedBlocks || ! isEmpty( before ) ) { - blocks.push( onSplit( this.valueToFormat( before ) ) ); - } - - if ( hasPastedBlocks ) { - blocks.push( ...pastedBlocks ); - } else if ( onSplitMiddle ) { - blocks.push( onSplitMiddle() ); - } - - // If there's pasted blocks, append a block with the content after the - // caret. Otherwise, do append and empty block if there is no - // `onSplitMiddle` prop, but if there is and the content is empty, the - // middle block is enough to set focus in. - if ( hasPastedBlocks || ! onSplitMiddle || ! isEmpty( after ) ) { - blocks.push( onSplit( this.valueToFormat( after ) ) ); - } - - // If there are pasted blocks, set the selection to the last one. - // Otherwise, set the selection to the second block. - const indexToSelect = hasPastedBlocks ? blocks.length - 1 : 1; - // The onSplit event can cause a content update event for this block. Such event should - // definitely be processed by our native components, since they have no knowledge of - // how the split works. Setting lastEventCount to undefined forces the native component to - // always update when provided with new content. - this.lastEventCount = undefined; - onReplace( blocks, indexToSelect ); - } - valueToFormat( value ) { // remove the outer root tags return this.removeRootTagsProduceByAztec( toHTMLString( { @@ -245,6 +163,7 @@ export class RichText extends Component { } onFormatChange( record ) { + this.getRecord( record ); const { start, end, activeFormats = [] } = record; const changeHandlers = pickBy( this.props, ( v, key ) => key.startsWith( 'format_on_change_functions_' ) @@ -346,92 +265,67 @@ export class RichText extends Component { this.lastAztecEventType = 'content size change'; } - onEnter( event ) { - if ( this.props.onEnter ) { - this.props.onEnter(); + handleEnter( event ) { + const { onEnter } = this.props; + + if ( ! onEnter ) { return; } - const { - __unstableOnReplace: onReplace, - __unstableOnSplit: onSplit, - } = this.props; - this.lastEventCount = event.nativeEvent.eventCount; - this.comesFromAztec = true; - this.firedAfterTextChanged = event.nativeEvent.firedAfterTextChanged; - - const canSplit = onReplace && onSplit; - const currentRecord = this.createRecord(); - if ( this.multilineTag ) { - if ( event.shiftKey ) { - this.needsSelectionUpdate = true; - const insertedLineBreak = { ...insert( currentRecord, '\n' ) }; - this.onFormatChange( insertedLineBreak ); - } else if ( canSplit && isEmptyLine( currentRecord ) ) { - this.onSplit( currentRecord ); - } else { - this.needsSelectionUpdate = true; - const insertedLineSeparator = { ...insertLineSeparator( currentRecord ) }; - this.onFormatChange( insertedLineSeparator ); - } - } else if ( event.shiftKey || ! onSplit ) { - this.needsSelectionUpdate = true; - const insertedLineBreak = { ...insert( currentRecord, '\n' ) }; - this.onFormatChange( insertedLineBreak ); - } else { - this.onSplit( currentRecord ); - } + onEnter( { + value: this.createRecord(), + onChange: this.onFormatChange, + shiftKey: event.shiftKey, + } ); this.lastAztecEventType = 'input'; } - onBackspace( event ) { - const { - __unstableOnMerge: onMerge, - __unstableOnRemove: onRemove, - onChange, - } = this.props; - if ( ! onMerge && ! onRemove ) { - return; - } - + handleDelete( event ) { const keyCode = BACKSPACE; // TODO : should we differentiate BACKSPACE and DELETE? const isReverse = keyCode === BACKSPACE; + const { onDelete, __unstableMultilineTag: multilineTag } = this.props; + const { activeFormats = [] } = this.state; this.lastEventCount = event.nativeEvent.eventCount; this.comesFromAztec = true; this.firedAfterTextChanged = event.nativeEvent.firedAfterTextChanged; const value = this.createRecord(); - const { start, end } = value; + const { start, end, text } = value; let newValue; // Always handle full content deletion ourselves. - if ( start === 0 && end !== 0 && end >= value.text.length ) { - newValue = remove( value, start, end ); - onChange( newValue ); + if ( start === 0 && end !== 0 && end >= text.length ) { + newValue = remove( value ); + this.onFormatChange( newValue ); + event.preventDefault(); return; } - if ( this.multilineTag ) { - newValue = removeLineSeparator( value, keyCode === BACKSPACE ); + if ( multilineTag ) { + if ( isReverse && value.start === 0 && value.end === 0 && isEmptyLine( value ) ) { + newValue = removeLineSeparator( value, ! isReverse ); + } else { + newValue = removeLineSeparator( value, isReverse ); + } if ( newValue ) { this.onFormatChange( newValue ); + event.preventDefault(); return; } } - const empty = this.isEmpty(); - - if ( onMerge ) { - onMerge( ! isReverse ); + // Only process delete if the key press occurs at an uncollapsed edge. + if ( + ! onDelete || + ! isCollapsed( value ) || + activeFormats.length || + ( isReverse && start !== 0 ) || + ( ! isReverse && end !== text.length ) + ) { + return; } - // Only handle remove on Backspace. This serves dual-purpose of being - // an intentional user interaction distinguishing between Backspace and - // Delete to remove the empty field, but also to avoid merge & remove - // causing destruction of two fields (merge, then removed merged). - if ( onRemove && empty && isReverse ) { - onRemove( ! isReverse ); - } + onDelete( { isReverse, value } ); event.preventDefault(); this.lastAztecEventType = 'input'; @@ -444,10 +338,7 @@ export class RichText extends Component { */ onPaste( event ) { const { - tagName, - __unstablePasteHandler: pasteHandler, - __unstableOnReplace: onReplace, - __unstableOnSplit: onSplit, + onPaste, onChange, } = this.props; @@ -456,30 +347,6 @@ export class RichText extends Component { event.preventDefault(); - // Only process file if no HTML is present. - // Note: a pasted file may have the URL as plain text. - if ( files && files.length > 0 ) { - const uploadId = Number.MAX_SAFE_INTEGER; - let html = ''; - files.forEach( ( file ) => { - html += `<img src="${ file }" class="wp-image-${ uploadId }">`; - } ); - const content = pasteHandler( { - HTML: html, - mode: 'BLOCKS', - tagName, - } ); - const shouldReplace = onReplace && this.isEmpty(); - - if ( shouldReplace ) { - onReplace( content ); - } else { - this.onSplit( currentRecord, content ); - } - - return; - } - // There is a selection, check if a URL is pasted. if ( ! isCollapsed( currentRecord ) ) { const trimmedText = ( pastedHtml || pastedText ).replace( /<[^>]+>/g, '' ) @@ -503,46 +370,14 @@ export class RichText extends Component { } } - const shouldReplace = this.props.onReplace && this.isEmpty(); - - let mode = 'INLINE'; - - if ( shouldReplace ) { - mode = 'BLOCKS'; - } else if ( onSplit ) { - mode = 'AUTO'; - } - - const pastedContent = saferPasteHandler( pasteHandler, { - HTML: pastedHtml, - plainText: pastedText, - mode, - tagName: this.props.tagName, - canUserUseUnfilteredHTML: this.props.canUserUseUnfilteredHTML, - } ); - - if ( typeof pastedContent === 'string' ) { - const recordToInsert = create( { html: pastedContent } ); - const resultingRecord = insert( currentRecord, recordToInsert ); - const resultingContent = this.valueToFormat( resultingRecord ); - - this.lastEventCount = undefined; - this.value = resultingContent; - - // explicitly set selection after inline paste - this.onSelectionChange( resultingRecord.start, resultingRecord.end ); - - onChange( this.value ); - } else if ( onSplit ) { - if ( ! pastedContent.length ) { - return; - } - - if ( shouldReplace ) { - onReplace( pastedContent ); - } else { - this.onSplit( currentRecord, pastedContent ); - } + if ( onPaste ) { + onPaste( { + value: currentRecord, + onChange: this.onFormatChange, + html: pastedHtml, + plainText: pastedText, + files, + } ); } } @@ -759,7 +594,7 @@ export class RichText extends Component { this.lastEventCount = undefined; // force a refresh on the native side value = ''; } - // On android if content is empty we need to send no content or else the placeholder with not show. + // On android if content is empty we need to send no content or else the placeholder will not show. if ( ! this.isIOS && value === '' ) { return value; } @@ -852,8 +687,8 @@ export class RichText extends Component { onChange={ this.onChange } onFocus={ this.onFocus } onBlur={ this.onBlur } - onEnter={ this.onEnter } - onBackspace={ this.onBackspace } + onEnter={ this.handleEnter } + onBackspace={ this.handleDelete } onPaste={ this.onPaste } activeFormats={ this.getActiveFormatNames( record ) } onContentSizeChange={ this.onContentSizeChange } diff --git a/packages/rich-text/src/component/test/index.native.js b/packages/rich-text/src/component/test/index.native.js index 6b2bc12f855ffa..22ee6b118bb2d2 100644 --- a/packages/rich-text/src/component/test/index.native.js +++ b/packages/rich-text/src/component/test/index.native.js @@ -1,17 +1,8 @@ -/** - * External dependencies - */ -import { shallow } from 'enzyme'; - /** * Internal dependencies */ import { RichText } from '../index'; -const getStylesFromColorScheme = () => { - return { color: 'white' }; -}; - describe( 'RichText Native', () => { let richText; @@ -33,29 +24,4 @@ describe( 'RichText Native', () => { expect( richText.willTrimSpaces( html ) ).toBe( false ); } ); } ); - - describe( 'Adds new line on Enter', () => { - let newValue; - const wrapper = shallow( <RichText - rootTagsToEliminate={ [ 'p' ] } - value="" - onChange={ ( value ) => { - newValue = value; - } } - formatTypes={ [] } - onSelectionChange={ jest.fn() } - getStylesFromColorScheme={ getStylesFromColorScheme } - /> ); - - const event = { - nativeEvent: { - eventCount: 0, - }, - }; - wrapper.instance().onEnter( event ); - - it( ' Adds <br> tag to content after pressing Enter key', () => { - expect( newValue ).toEqual( '<br>' ); - } ); - } ); } ); diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index e6efc99f5fec97..84a158167b860f 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -10,16 +10,25 @@ import { select, dispatch, withSelect, withDispatch } from '@wordpress/data'; import { addFilter } from '@wordpress/hooks'; import { compose } from '@wordpress/compose'; +/** + * @typedef {Object} WPFormat + * + * @property {string} name A string identifying the format. Must be + * unique across all registered formats. + * @property {string} tagName The HTML tag this format will wrap the + * selection with. + * @property {string} [className] A class to match the format. + * @property {string} title Name of the format. + * @property {Function} edit Should return a component for the user to + * interact with the new registered format. + */ + /** * Registers a new format provided a unique name and an object defining its * behavior. * * @param {string} name Format name. - * @param {Object} settings Format settings. - * @param {string} settings.tagName The HTML tag this format will wrap the selection with. - * @param {string} [settings.className] A class to match the format. - * @param {string} settings.title Name of the format. - * @param {Function} settings.edit Should return a component for the user to interact with the new registered format. + * @param {WPFormat} settings Format settings. * * @return {WPFormat|undefined} The format, if it has been successfully registered; * otherwise `undefined`. diff --git a/packages/rich-text/src/test/helpers/index.js b/packages/rich-text/src/test/helpers/index.js index fe97f00d245f53..604ca0c518ec8a 100644 --- a/packages/rich-text/src/test/helpers/index.js +++ b/packages/rich-text/src/test/helpers/index.js @@ -4,7 +4,7 @@ import { ZWNBSP } from '../../special-characters'; export function getSparseArrayLength( array ) { - return array.reduce( ( i ) => i + 1, 0 ); + return array.reduce( ( accumulator ) => accumulator + 1, 0 ); } const em = { type: 'em' }; diff --git a/packages/rich-text/src/to-dom.js b/packages/rich-text/src/to-dom.js index 9d7aed3cfdfd00..730a38587114c4 100644 --- a/packages/rich-text/src/to-dom.js +++ b/packages/rich-text/src/to-dom.js @@ -67,7 +67,7 @@ function getNodeByPath( node, path ) { * each call to `createEmpty`. Therefore, you should not hold a reference to * the value to operate upon asynchronously, as it may have unexpected results. * - * @return {WPRichTextTree} RichText tree. + * @return {Object} RichText tree. */ const createEmpty = () => createElement( document, '' ); diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 96e7ae747107c8..66d7acb086a16b 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -1,5 +1,15 @@ ## Master +### Breaking Changes + +- The bundled `npm-package-json-lint` dependency has been updated from requiring `^3.6.0` to requiring `^4.0.3` ([#18054](https://github.com/WordPress/gutenberg/pull/18054)). Please see the [migration guide](https://npmpackagejsonlint.org/docs/en/v3-to-v4). Note: `npmPackageJsonLintConfig` prop in the `package.json` file needs to be renamed to `npmpackagejsonlint`. + +### New Features + +- The bundled `puppeteer` dependency has been updated from requiring `^1.19.0` to requiring `^1.20.0` ([#18054](https://github.com/WordPress/gutenberg/pull/18054)). It uses Chromium v78 instead of Chromium v77. + +## 5.1.0 + ### New Features - The bundled `webpack` dependency has been updated from requiring `4.8.3` to requiring `^4.41.0` ([#17746](https://github.com/WordPress/gutenberg/pull/17746)). diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 70d180fb0e2514..cc64a8fb5a713d 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/scripts", - "version": "5.0.0", + "version": "5.1.0", "description": "Collection of reusable scripts for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -50,8 +50,8 @@ "js-yaml": "^3.13.1", "lodash": "^4.17.15", "minimist": "^1.2.0", - "npm-package-json-lint": "^3.6.0", - "puppeteer": "^1.19.0", + "npm-package-json-lint": "^4.0.3", + "puppeteer": "^1.20.0", "read-pkg-up": "^1.0.1", "request": "^2.88.0", "resolve-bin": "^0.4.0", diff --git a/packages/scripts/scripts/lint-pkg-json.js b/packages/scripts/scripts/lint-pkg-json.js index 487a8191067dbc..53e9c42b366437 100644 --- a/packages/scripts/scripts/lint-pkg-json.js +++ b/packages/scripts/scripts/lint-pkg-json.js @@ -25,6 +25,8 @@ const hasLintConfig = hasArgInCLI( '-c' ) || hasArgInCLI( '--configFile' ) || hasProjectFile( '.npmpackagejsonlintrc.json' ) || hasProjectFile( 'npmpackagejsonlint.config.js' ) || + hasPackageProp( 'npmpackagejsonlint' ) || + // npm-package-json-lint v3.x used a different prop name. hasPackageProp( 'npmPackageJsonLintConfig' ); const defaultConfigArgs = ! hasLintConfig ? diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index 99aa8807a8bf6c..39941d137650bf 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/server-side-render", - "version": "1.3.0", + "version": "1.3.3", "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/url/package.json b/packages/url/package.json index 9c0b1bcaeba066..bc1e8bf3587eb0 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "2.8.0", + "version": "2.8.2", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/url/src/index.js b/packages/url/src/index.js index b4fa34efac51a6..2b549d6ba1dc4e 100644 --- a/packages/url/src/index.js +++ b/packages/url/src/index.js @@ -360,6 +360,10 @@ export function removeQueryArgs( url, ...args ) { * @return {string} The updated URL. */ export function prependHTTP( url ) { + if ( ! url ) { + return url; + } + url = url.trim(); if ( ! USABLE_HREF_REGEXP.test( url ) && ! EMAIL_REGEXP.test( url ) ) { return 'http://' + url; diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 5dd1af95cbe842..5cedb8e675caa4 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "2.8.0", + "version": "2.8.2", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index 7201fbdc9760bc..7664cf8b99a0fc 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/wordcount", - "version": "2.6.0", + "version": "2.6.2", "description": "WordPress word count utility.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/phpunit/class-override-script-test.php b/phpunit/class-override-script-test.php index 034fa2f25a618b..074de00b649ba6 100644 --- a/phpunit/class-override-script-test.php +++ b/phpunit/class-override-script-test.php @@ -28,7 +28,10 @@ function tearDown() { * Tests that script is localized. */ function test_localizes_script() { + global $wp_scripts; + gutenberg_override_script( + $wp_scripts, 'gutenberg-dummy-script', 'https://example.com/', array( 'dependency' ), @@ -36,7 +39,6 @@ function test_localizes_script() { false ); - global $wp_scripts; $script = $wp_scripts->query( 'gutenberg-dummy-script', 'registered' ); $this->assertEquals( array( 'dependency', 'wp-i18n' ), $script->deps ); } @@ -45,7 +47,10 @@ function test_localizes_script() { * Tests that script properties are overridden. */ function test_replaces_registered_properties() { + global $wp_scripts; + gutenberg_override_script( + $wp_scripts, 'gutenberg-dummy-script', 'https://example.com/updated', array( 'updated-dependency' ), @@ -53,19 +58,21 @@ function test_replaces_registered_properties() { true ); - global $wp_scripts; $script = $wp_scripts->query( 'gutenberg-dummy-script', 'registered' ); $this->assertEquals( 'https://example.com/updated', $script->src ); $this->assertEquals( array( 'updated-dependency', 'wp-i18n' ), $script->deps ); $this->assertEquals( 'updated-version', $script->ver ); - $this->assertEquals( 1, $script->extra['group'] ); + $this->assertTrue( $script->args ); } /** * Tests that new script registers normally if no handle by the name. */ function test_registers_new_script() { + global $wp_scripts; + gutenberg_override_script( + $wp_scripts, 'gutenberg-second-dummy-script', 'https://example.com/', array( 'dependency' ), @@ -73,11 +80,10 @@ function test_registers_new_script() { true ); - global $wp_scripts; $script = $wp_scripts->query( 'gutenberg-second-dummy-script', 'registered' ); $this->assertEquals( 'https://example.com/', $script->src ); $this->assertEquals( array( 'dependency', 'wp-i18n' ), $script->deps ); $this->assertEquals( 'version', $script->ver ); - $this->assertEquals( 1, $script->extra['group'] ); + $this->assertTrue( $script->args ); } } diff --git a/playground/.sassrc b/playground/.sassrc new file mode 100644 index 00000000000000..4f704b80b310a5 --- /dev/null +++ b/playground/.sassrc @@ -0,0 +1,5 @@ +{ + "includePaths": [ + "node_modules" + ] +} diff --git a/playground/src/index.js b/playground/src/index.js index b74e30091c1739..66815d8a027256 100644 --- a/playground/src/index.js +++ b/playground/src/index.js @@ -8,10 +8,12 @@ import { BlockEditorKeyboardShortcuts, BlockEditorProvider, BlockList, + BlockInspector, WritingFlow, ObserveTyping, } from '@wordpress/block-editor'; import { + Button, Popover, SlotFillProvider, DropZoneProvider, @@ -40,6 +42,9 @@ function App() { <Fragment> <div className="playground__header"> <h1 className="playground__logo">Gutenberg Playground</h1> + <Button isLarge href="design-system/components" target="_blank"> + Design System Components + </Button> </div> <div className="playground__body"> <SlotFillProvider> @@ -49,6 +54,9 @@ function App() { onInput={ updateBlocks } onChange={ updateBlocks } > + <div className="playground__sidebar"> + <BlockInspector /> + </div> <div className="editor-styles-wrapper"> <BlockEditorKeyboardShortcuts /> <WritingFlow> @@ -67,7 +75,4 @@ function App() { } registerCoreBlocks(); -render( - <App />, - document.querySelector( '#app' ) -); +render( <App />, document.querySelector( '#app' ) ); diff --git a/playground/src/style.scss b/playground/src/style.scss index 63a330e00a2d6f..b6fd8395ac08e8 100644 --- a/playground/src/style.scss +++ b/playground/src/style.scss @@ -1,17 +1,40 @@ -@import "../../assets/stylesheets/colors"; -@import "../../assets/stylesheets/variables"; -@import "../../assets/stylesheets/mixins"; -@import "../../assets/stylesheets/breakpoints"; -@import "../../assets/stylesheets/animations"; -@import "../../assets/stylesheets/z-index"; +@import "~@wordpress/base-styles/colors"; +@import "~@wordpress/base-styles/variables"; +@import "~@wordpress/base-styles/mixins"; +@import "~@wordpress/base-styles/breakpoints"; +@import "~@wordpress/base-styles/animations"; +@import "~@wordpress/base-styles/z-index"; @import "./reset"; @import "./editor-styles"; +$playground-header-height: 95px; .playground__header { - padding: 20px; + align-items: center; border-bottom: 1px solid #ddd; + display: flex; + justify-content: space-between; + padding: 20px; + height: $playground-header-height; +} + +.playground__sidebar { + position: fixed; + top: $playground-header-height; + right: 0; + bottom: 0; + width: $sidebar-width; + border-left: $border-width solid $light-gray-500; + height: auto; + overflow: auto; + -webkit-overflow-scrolling: touch; + + // Temporarily disable the sidebar on mobile + display: none; + @include break-small() { + display: block; + } } .playground__logo { @@ -20,6 +43,9 @@ } .playground__body { + @include break-small() { + width: calc(100% - #{$sidebar-width}); + } padding-top: 20px; img { diff --git a/test/integration/__snapshots__/blocks-raw-handling.test.js.snap b/test/integration/__snapshots__/blocks-raw-handling.test.js.snap index 29b4c95f5806b0..97c607d4d94866 100644 --- a/test/integration/__snapshots__/blocks-raw-handling.test.js.snap +++ b/test/integration/__snapshots__/blocks-raw-handling.test.js.snap @@ -72,3 +72,13 @@ exports[`rawHandler should convert a caption shortcode with link 1`] = ` <figure class=\\"wp-block-image alignnone\\"><a href=\\"http://build.wordpress-develop.test/wp-content/uploads/2011/07/100_5478.jpg\\"><img src=\\"http://build.wordpress-develop.test/wp-content/uploads/2011/07/100_5478.jpg?w=604\\" alt=\\"Bell on Wharf\\" class=\\"wp-image-754\\"/></a><figcaption>Bell on wharf in San Francisco</figcaption></figure> <!-- /wp:image -->" `; + +exports[`rawHandler should convert a list with attributes 1`] = ` +"<!-- wp:list {\\"ordered\\":true,\\"type\\":\\"i\\",\\"start\\":2,\\"reversed\\":true} --> +<ol type=\\"i\\" reversed start=\\"2\\"><li>1 + <ol start=\\"2\\" reversed=\\"\\" type=\\"i\\"> + <li>1</li> + </ol> + </li></ol> +<!-- /wp:list -->" +`; diff --git a/test/integration/blocks-raw-handling.test.js b/test/integration/blocks-raw-handling.test.js index fda576a042cbb3..228c6588ef96b5 100644 --- a/test/integration/blocks-raw-handling.test.js +++ b/test/integration/blocks-raw-handling.test.js @@ -287,4 +287,9 @@ describe( 'rawHandler', () => { const HTML = readFile( path.join( __dirname, 'fixtures/shortcode-caption-with-caption-link.html' ) ); expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); } ); + + it( 'should convert a list with attributes', () => { + const HTML = readFile( path.join( __dirname, 'fixtures/list-with-attributes.html' ) ); + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); } ); diff --git a/test/integration/fixtures/google-docs-out.html b/test/integration/fixtures/google-docs-out.html index 576b2cd7fda73f..54a1e07ac1349a 100644 --- a/test/integration/fixtures/google-docs-out.html +++ b/test/integration/fixtures/google-docs-out.html @@ -7,7 +7,7 @@ <h2>This is a <em>heading</em></h2> <!-- /wp:heading --> <!-- wp:paragraph --> -<p>Formatting test: <strong>bold</strong>, <em>italic</em>, <a href="https://w.org/">link</a>, strikethrough, <sup>superscript</sup>, <sub>subscript</sub>, <strong><em>nested</em></strong>.<br></p> +<p>Formatting test: <strong>bold</strong>, <em>italic</em>, <a href="https://w.org/">link</a>, <s>strikethrough</s>, <sup>superscript</sup>, <sub>subscript</sub>, <strong><em>nested</em></strong>.<br></p> <!-- /wp:paragraph --> <!-- wp:list --> diff --git a/test/integration/fixtures/google-docs-with-comments-out.html b/test/integration/fixtures/google-docs-with-comments-out.html index 576b2cd7fda73f..54a1e07ac1349a 100644 --- a/test/integration/fixtures/google-docs-with-comments-out.html +++ b/test/integration/fixtures/google-docs-with-comments-out.html @@ -7,7 +7,7 @@ <h2>This is a <em>heading</em></h2> <!-- /wp:heading --> <!-- wp:paragraph --> -<p>Formatting test: <strong>bold</strong>, <em>italic</em>, <a href="https://w.org/">link</a>, strikethrough, <sup>superscript</sup>, <sub>subscript</sub>, <strong><em>nested</em></strong>.<br></p> +<p>Formatting test: <strong>bold</strong>, <em>italic</em>, <a href="https://w.org/">link</a>, <s>strikethrough</s>, <sup>superscript</sup>, <sub>subscript</sub>, <strong><em>nested</em></strong>.<br></p> <!-- /wp:paragraph --> <!-- wp:list --> diff --git a/test/integration/fixtures/list-with-attributes.html b/test/integration/fixtures/list-with-attributes.html new file mode 100644 index 00000000000000..7114d2da756938 --- /dev/null +++ b/test/integration/fixtures/list-with-attributes.html @@ -0,0 +1,7 @@ +<ol start="2" reversed type="i"> + <li>1 + <ol start="2" reversed type="i"> + <li>1</li> + </ol> + </li> +</ol> diff --git a/test/integration/fixtures/ms-word-out.html b/test/integration/fixtures/ms-word-out.html index 427e12686ad625..47f7cab83ae73f 100644 --- a/test/integration/fixtures/ms-word-out.html +++ b/test/integration/fixtures/ms-word-out.html @@ -24,8 +24,8 @@ <h2>This is a heading level 2</h2> <ul><li>A</li><li>Bulleted<ul><li>Indented</li></ul></li><li>List</li></ul> <!-- /wp:list --> -<!-- wp:list {"ordered":true} --> -<ol><li>One</li><li>Two</li><li>Three</li></ol> +<!-- wp:list {"ordered":true,"type":"1"} --> +<ol type="1"><li>One</li><li>Two</li><li>Three</li></ol> <!-- /wp:list --> <!-- wp:table --> diff --git a/test/integration/full-content/full-content.test.js b/test/integration/full-content/full-content.test.js index 1a50c84a834503..cab70d134b3720 100644 --- a/test/integration/full-content/full-content.test.js +++ b/test/integration/full-content/full-content.test.js @@ -49,7 +49,11 @@ function normalizeParsedBlocks( blocks ) { describe( 'full post content fixture', () => { beforeAll( () => { unstable__bootstrapServerSideBlockDefinitions( require( './server-registered.json' ) ); - const settings = { __experimentalEnableLegacyWidgetBlock: true, __experimentalEnableMenuBlock: true }; + const settings = { + __experimentalEnableLegacyWidgetBlock: true, + __experimentalEnableMenuBlock: true, + __experimentalEnableFullSiteEditing: true, + }; // Load all hooks that modify blocks require( '../../../packages/editor/src/hooks' ); registerCoreBlocks(); diff --git a/test/integration/full-content/server-registered.json b/test/integration/full-content/server-registered.json index e11fe0283b44a5..9d17c64f5d6ba5 100644 --- a/test/integration/full-content/server-registered.json +++ b/test/integration/full-content/server-registered.json @@ -1 +1 @@ -{"core\/archives":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/block":{"attributes":{"ref":{"type":"number"}}},"core\/calendar":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"month":{"type":"integer"},"year":{"type":"integer"}}},"core\/categories":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showHierarchy":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/latest-comments":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"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}}},"core\/latest-posts":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"categories":{"type":"string"},"postsToShow":{"type":"number","default":5},"displayPostContent":{"type":"boolean","default":false},"displayPostContentRadio":{"type":"string","default":"excerpt"},"excerptLength":{"type":"number","default":55},"displayPostDate":{"type":"boolean","default":false},"postLayout":{"type":"string","default":"list"},"columns":{"type":"number","default":3},"order":{"type":"string","default":"desc"},"orderBy":{"type":"string","default":"date"}}},"core\/legacy-widget":{"attributes":{"identifier":{"type":"string"},"instance":{"type":"object"},"isCallbackWidget":{"type":"boolean"}}},"core\/navigation-menu":{"category":"layout","attributes":{"automaticallyAdd":{"type":"boolean","default":false}}},"core\/rss":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"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":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"label":{"type":"string","default":"Search"},"placeholder":{"type":"string","default":""},"buttonText":{"type":"string","default":"Search"}}},"core\/shortcode":{"attributes":{"text":{"type":"string","source":"html"}}},"core\/tag-cloud":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"taxonomy":{"type":"string","default":"post_tag"},"showTagCounts":{"type":"boolean","default":false}}}} \ No newline at end of file +{"core\/archives":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/block":{"attributes":{"ref":{"type":"number"}}},"core\/calendar":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"month":{"type":"integer"},"year":{"type":"integer"}}},"core\/categories":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"displayAsDropdown":{"type":"boolean","default":false},"showHierarchy":{"type":"boolean","default":false},"showPostCounts":{"type":"boolean","default":false}}},"core\/latest-comments":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"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}}},"core\/latest-posts":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"categories":{"type":"string"},"postsToShow":{"type":"number","default":5},"displayPostContent":{"type":"boolean","default":false},"displayPostContentRadio":{"type":"string","default":"excerpt"},"excerptLength":{"type":"number","default":55},"displayPostDate":{"type":"boolean","default":false},"postLayout":{"type":"string","default":"list"},"columns":{"type":"number","default":3},"order":{"type":"string","default":"desc"},"orderBy":{"type":"string","default":"date"}}},"core\/legacy-widget":{"attributes":{"identifier":{"type":"string"},"instance":{"type":"object"},"isCallbackWidget":{"type":"boolean"}}},"core\/navigation-menu":{"category":"layout","attributes":{"automaticallyAdd":{"type":"boolean","default":false}}},"core\/rss":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"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":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"label":{"type":"string","default":"Search"},"placeholder":{"type":"string","default":""},"buttonText":{"type":"string","default":"Search"}}},"core\/shortcode":{"attributes":{"text":{"type":"string","source":"html"}}},"core\/social-link-amazon":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"amazon"}}},"core\/social-link-bandcamp":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"bandcamp"}}},"core\/social-link-behance":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"behance"}}},"core\/social-link-chain":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"chain"}}},"core\/social-link-codepen":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"codepen"}}},"core\/social-link-deviantart":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"deviantart"}}},"core\/social-link-dribbble":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"dribbble"}}},"core\/social-link-dropbox":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"dropbox"}}},"core\/social-link-etsy":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"etsy"}}},"core\/social-link-facebook":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"facebook"}}},"core\/social-link-feed":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"feed"}}},"core\/social-link-fivehundredpx":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"fivehundredpx"}}},"core\/social-link-flickr":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"flickr"}}},"core\/social-link-foursquare":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"foursquare"}}},"core\/social-link-goodreads":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"goodreads"}}},"core\/social-link-google":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"google"}}},"core\/social-link-github":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"github"}}},"core\/social-link-instagram":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"instagram"}}},"core\/social-link-lastfm":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"lastfm"}}},"core\/social-link-linkedin":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"linkedin"}}},"core\/social-link-mail":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"mail"}}},"core\/social-link-mastodon":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"mastodon"}}},"core\/social-link-meetup":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"meetup"}}},"core\/social-link-medium":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"medium"}}},"core\/social-link-pinterest":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"pinterest"}}},"core\/social-link-pocket":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"pocket"}}},"core\/social-link-reddit":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"reddit"}}},"core\/social-link-skype":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"skype"}}},"core\/social-link-snapchat":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"snapchat"}}},"core\/social-link-soundcloud":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"soundcloud"}}},"core\/social-link-spotify":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"spotify"}}},"core\/social-link-tumblr":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"tumblr"}}},"core\/social-link-twitch":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"twitch"}}},"core\/social-link-twitter":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"twitter"}}},"core\/social-link-vimeo":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"vimeo"}}},"core\/social-link-vk":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"vk"}}},"core\/social-link-wordpress":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"wordpress"}}},"core\/social-link-yelp":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"yelp"}}},"core\/social-link-youtube":{"attributes":{"url":{"type":"string"},"site":{"type":"string","default":"youtube"}}},"core\/tag-cloud":{"attributes":{"align":{"type":"string","enum":["left","center","right","wide","full"]},"className":{"type":"string"},"taxonomy":{"type":"string","default":"post_tag"},"showTagCounts":{"type":"boolean","default":false}}}} \ No newline at end of file diff --git a/test/integration/shortcode-converter.test.js b/test/integration/shortcode-converter.test.js index 2f0e5ab632fa74..1b0ce7c30947d9 100644 --- a/test/integration/shortcode-converter.test.js +++ b/test/integration/shortcode-converter.test.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { registerCoreBlocks } from '@wordpress/block-library'; -import { createBlock } from '@wordpress/blocks'; +import { createBlock, registerBlockType } from '@wordpress/blocks'; /** * Internal dependencies @@ -12,6 +12,34 @@ import segmentHTMLToShortcodeBlock from '../../packages/blocks/src/api/raw-handl describe( 'segmentHTMLToShortcodeBlock', () => { beforeAll( () => { registerCoreBlocks(); + registerBlockType( 'test/gallery', { + title: 'Test Gallery', + category: 'common', + attributes: { + ids: { + type: 'array', + default: [], + }, + }, + transforms: { + from: [ + { + type: 'shortcode', + tag: [ 'my-gallery', 'my-bunch-of-images' ], + attributes: { + ids: { + type: 'array', + shortcode: ( { named: { ids } } ) => + ids.split( ',' ).map( ( id ) => ( + parseInt( id, 10 ) + ) ), + }, + }, + }, + ], + }, + save: () => null, + } ); } ); it( 'should convert a standalone shortcode between two paragraphs', () => { @@ -101,4 +129,15 @@ describe( 'segmentHTMLToShortcodeBlock', () => { expect( transformed[ 8 ] ).toEqual( '</p>' ); expect( transformed ).toHaveLength( 9 ); } ); + + it( 'should convert regardless of shortcode alias', () => { + const original = `<p>[my-gallery ids="1,2,3"]</p> +<p>[my-bunch-of-images ids="4,5,6"]</p>`; + const transformed = segmentHTMLToShortcodeBlock( original, 0 ); + expect( transformed[ 0 ] ).toBe( '<p>' ); + expect( transformed[ 1 ] ).toHaveProperty( 'name', 'test/gallery' ); + expect( transformed[ 2 ] ).toBe( '</p>\n<p>' ); + expect( transformed[ 3 ] ).toHaveProperty( 'name', 'test/gallery' ); + expect( transformed[ 4 ] ).toBe( '</p>' ); + } ); } ); diff --git a/test/native/setup.js b/test/native/setup.js index 60fe7194c0aa60..f1e507f38ba399 100644 --- a/test/native/setup.js +++ b/test/native/setup.js @@ -19,6 +19,8 @@ jest.mock( 'react-native-gutenberg-bridge', () => { requestMediaPickFromMediaLibrary: jest.fn(), requestMediaPickFromDeviceLibrary: jest.fn(), requestMediaPickFromDeviceCamera: jest.fn(), + getOtherMediaOptions: jest.fn(), + requestOtherMediaPickFrom: jest.fn(), }; } );