diff --git a/.eslintrc.js b/.eslintrc.js index 45fc4c94140f1c..feced45620657e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,14 +19,8 @@ const majorMinorRegExp = escapeRegExp( version.replace( /\.\d+$/, '' ) ) + '(\\. module.exports = { root: true, extends: [ - './eslint/config.js', - 'plugin:jest/recommended' - ], - env: { - 'jest/globals': true, - }, - plugins: [ - 'jest', + '@wordpress/eslint-config', + 'plugin:jest/recommended', ], rules: { 'no-restricted-syntax': [ @@ -189,9 +183,13 @@ module.exports = { overrides: [ { files: [ 'test/e2e/**/*.js' ], + env: { + browser: true, + }, globals: { - page: true, browser: true, + page: true, + wp: true, }, }, ], diff --git a/docs/manifest.json b/docs/manifest.json index a15c102355f36c..398f6445c56ae2 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -443,6 +443,12 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/escape-html/README.md", "parent": "packages" }, + { + "title": "@wordpress/eslint-config", + "slug": "packages-eslint-config", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/eslint-config/README.md", + "parent": "packages" + }, { "title": "@wordpress/format-library", "slug": "packages-format-library", diff --git a/gutenberg.php b/gutenberg.php index 327a6dba8e76fc..084de1ef1a84f0 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: 4.5.1 + * Version: 4.6.0 * Author: Gutenberg Team * * @package gutenberg diff --git a/package-lock.json b/package-lock.json index ab4562395d1a18..b0c66aaf0ab234 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.5.1", + "version": "4.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2590,6 +2590,15 @@ "@babel/runtime": "^7.0.0" } }, + "@wordpress/eslint-config": { + "version": "file:packages/eslint-config", + "dev": true, + "requires": { + "babel-eslint": "^8.0.3", + "eslint-plugin-jsx-a11y": "6.0.2", + "eslint-plugin-react": "7.7.0" + } + }, "@wordpress/format-library": { "version": "file:packages/format-library", "requires": { @@ -3055,16 +3064,6 @@ } } }, - "aria-query": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.1.tgz", - "integrity": "sha1-Jsu1r/ZBRLCoJb4YRuCxbPoAsR4=", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -3323,15 +3322,6 @@ "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", "dev": true }, - "axobject-query": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", - "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7" - } - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -5671,15 +5661,6 @@ "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==", "dev": true }, - "comment-parser": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.4.2.tgz", - "integrity": "sha1-+lo/eAEwcBFIZtx7jpzzF6ljX3Q=", - "dev": true, - "requires": { - "readable-stream": "^2.0.4" - } - }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -7565,34 +7546,12 @@ } } }, - "eslint-config-wordpress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-wordpress/-/eslint-config-wordpress-2.0.0.tgz", - "integrity": "sha1-UgEgbGlk1kgxUjLt9t+9LpJeTNY=", - "dev": true - }, - "eslint-plugin-i18n": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-i18n/-/eslint-plugin-i18n-1.2.0.tgz", - "integrity": "sha1-SfP0OA7e/oyHbwyXlh9lw6w3zao=", - "dev": true - }, "eslint-plugin-jest": { "version": "21.5.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-21.5.0.tgz", "integrity": "sha512-4fxfe2RcqzU+IVNQL5n4pqibLcUhKKxihYsA510+6kC/FTdGInszDDHgO4ntBzPWu8mcHAvKJLs8o3AQw6eHTg==", "dev": true }, - "eslint-plugin-jsdoc": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-3.5.0.tgz", - "integrity": "sha512-qoNpVicVWGjGBXAJsqRoqVuAnajgX7PWtSa2Men36XKRiXe3RS/QmRv215PXZwo4OHskYOsUoJUeiPiWtS9ULA==", - "dev": true, - "requires": { - "comment-parser": "^0.4.2", - "lodash": "^4.17.4" - } - }, "eslint-plugin-jsx-a11y": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.0.2.tgz", @@ -7606,23 +7565,38 @@ "damerau-levenshtein": "^1.0.0", "emoji-regex": "^6.1.0", "jsx-ast-utils": "^1.4.0" - } - }, - "eslint-plugin-node": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz", - "integrity": "sha512-Q/Cc2sW1OAISDS+Ji6lZS2KV4b7ueA/WydVWd1BECTQwVvfQy5JAi3glhINoKzoMnfnuRgNP+ZWKrGAbp3QDxw==", - "dev": true, - "requires": { - "ignore": "^3.3.6", - "minimatch": "^3.0.4", - "resolve": "^1.3.3", - "semver": "^5.4.1" + }, + "dependencies": { + "aria-query": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.1.tgz", + "integrity": "sha1-Jsu1r/ZBRLCoJb4YRuCxbPoAsR4=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "axobject-query": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", + "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "jsx-ast-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", + "dev": true + } } }, "eslint-plugin-react": { "version": "7.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz", + "resolved": "http://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz", "integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==", "dev": true, "requires": { @@ -7632,15 +7606,6 @@ "prop-types": "^15.6.0" }, "dependencies": { - "jsx-ast-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", - "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", - "dev": true, - "requires": { - "array-includes": "^3.0.3" - } - }, "prop-types": { "version": "15.6.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", @@ -7653,27 +7618,6 @@ } } }, - "eslint-plugin-wordpress": { - "version": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1", - "from": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1", - "dev": true, - "requires": { - "eslint-plugin-i18n": "~1.2.0", - "eslint-plugin-jsdoc": "~3.5.0", - "eslint-plugin-node": "~6.0.1", - "eslint-plugin-wpcalypso": "~4.0.1", - "merge": "~1.2.0" - } - }, - "eslint-plugin-wpcalypso": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-wpcalypso/-/eslint-plugin-wpcalypso-4.0.1.tgz", - "integrity": "sha512-fU5NSc0XGdel/tlEIUoESOdqphBWQN2FfSgXXNHpXKX7ftTcqXacqgzXU8OVziyhXz6s2RUzK0+JSJaNxhZ+Mw==", - "dev": true, - "requires": { - "requireindex": "^1.1.0" - } - }, "eslint-scope": { "version": "3.7.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", @@ -13298,10 +13242,13 @@ } }, "jsx-ast-utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", - "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", - "dev": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", + "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", + "dev": true, + "requires": { + "array-includes": "^3.0.3" + } }, "keyv": { "version": "3.0.0", @@ -18617,12 +18564,6 @@ } } }, - "requireindex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", - "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", - "dev": true - }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", diff --git a/package.json b/package.json index 41b226272d1bad..fc4236a9b3910e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.5.1", + "version": "4.6.0", "private": true, "description": "A new WordPress editor experience", "repository": "git+https://github.com/WordPress/gutenberg.git", @@ -62,6 +62,7 @@ "@wordpress/babel-preset-default": "file:packages/babel-preset-default", "@wordpress/browserslist-config": "file:packages/browserslist-config", "@wordpress/custom-templated-path-webpack-plugin": "file:packages/custom-templated-path-webpack-plugin", + "@wordpress/eslint-config": "file:packages/eslint-config", "@wordpress/jest-console": "file:packages/jest-console", "@wordpress/jest-preset-default": "file:packages/jest-preset-default", "@wordpress/library-export-default-webpack-plugin": "file:packages/library-export-default-webpack-plugin", @@ -80,11 +81,7 @@ "deasync": "0.1.13", "deep-freeze": "0.0.1", "doctrine": "2.1.0", - "eslint-config-wordpress": "2.0.0", "eslint-plugin-jest": "21.5.0", - "eslint-plugin-jsx-a11y": "6.0.2", - "eslint-plugin-react": "7.7.0", - "eslint-plugin-wordpress": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1", "espree": "3.5.4", "glob": "7.1.2", "husky": "0.14.3", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 5f6dbcd09b9321..22d04b8f506ee2 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "2.2.7", + "version": "2.2.8", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/src/classic/edit.js b/packages/block-library/src/classic/edit.js index db1467b6301399..2502c0f7e0eb5f 100644 --- a/packages/block-library/src/classic/edit.js +++ b/packages/block-library/src/classic/edit.js @@ -5,6 +5,8 @@ import { Component } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; import { BACKSPACE, DELETE, F10 } from '@wordpress/keycodes'; +const { wp } = window; + function isTmceEmpty( editor ) { // When tinyMce is empty the content seems to be: //


diff --git a/packages/components/package.json b/packages/components/package.json index 8e470335fa50a6..6657f37b182b61 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "7.0.2", + "version": "7.0.3", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 18139501460cc3..d496bb6162e239 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -1,3 +1,10 @@ +## 4.1.0 (Unreleased) + +### New Feature + +- `withDispatch`'s `mapDispatchToProps` function takes the `registry` object as the 3rd param ([#11851](https://github.com/WordPress/gutenberg/pull/11851)). +- `withSelect`'s `mapSelectToProps` function takes the `registry` object as the 3rd param ([#11851](https://github.com/WordPress/gutenberg/pull/11851)). + ## 4.0.1 (2018-11-20) ## 4.0.0 (2018-11-15) diff --git a/packages/data/README.md b/packages/data/README.md index f6ca4c665c8fff..b5745e7719ed7e 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -229,7 +229,7 @@ A higher-order component is a function which accepts a [component](https://githu #### `withSelect( mapSelectToProps: Function ): Function` -Use `withSelect` to inject state-derived props into a component. Passed a function which returns an object mapping prop names to the subscribed data source, a higher-order component function is returned. The higher-order component can be used to enhance a presentational component, updating it automatically when state changes. The mapping function is passed the [`select` function](#select) and the props passed to the original component. +Use `withSelect` to inject state-derived props into a component. Passed a function which returns an object mapping prop names to the subscribed data source, a higher-order component function is returned. The higher-order component can be used to enhance a presentational component, updating it automatically when state changes. The mapping function is passed the [`select` function](#select), the props passed to the original component and the `registry` object. _Example:_ @@ -261,7 +261,7 @@ In the above example, when `HammerPriceDisplay` is rendered into an application, #### `withDispatch( mapDispatchToProps: Function ): Function` -Use `withDispatch` to inject dispatching action props into your component. Passed a function which returns an object mapping prop names to action dispatchers, a higher-order component function is returned. The higher-order component can be used to enhance a component. For example, you can define callback behaviors as props for responding to user interactions. The mapping function is passed the [`dispatch` function](#dispatch) and the props passed to the original component. +Use `withDispatch` to inject dispatching action props into your component. Passed a function which returns an object mapping prop names to action dispatchers, a higher-order component function is returned. The higher-order component can be used to enhance a component. For example, you can define callback behaviors as props for responding to user interactions. The mapping function is passed the [`dispatch` function](#dispatch), the props passed to the original component and the `registry` object. ```jsx function Button( { onClick, children } ) { @@ -272,7 +272,7 @@ const { withDispatch } = wp.data; const SaleButton = withDispatch( ( dispatch, ownProps ) => { const { startSale } = dispatch( 'my-shop' ); - const { discountPercent = 20 } = ownProps; + const { discountPercent } = ownProps; return { onClick() { @@ -281,6 +281,33 @@ const SaleButton = withDispatch( ( dispatch, ownProps ) => { }; } )( Button ); +// Rendered in the application: +// +// Start Sale! +``` + +In the majority of cases, it will be sufficient to use only two first params passed to `mapDispatchToProps` as illustrated in the previous example. However, there might be some very advanced use cases where using the `registry` object might be used as a tool to optimize the performance of your component. Using `select` function from the registry might be useful when you need to fetch some dynamic data from the store at the time when the event is fired, but at the same time, you never use it to render your component. In such scenario, you can avoid using the `withSelect` higher order component to compute such prop, which might lead to unnecessary re-renders of you component caused by its frequent value change. Keep in mind, that `mapDispatchToProps` must return an object with functions only. + +```jsx +function Button( { onClick, children } ) { + return ; +} + +const { withDispatch } = wp.data; + +const SaleButton = withDispatch( ( dispatch, ownProps, { select } ) => { + // Stock number changes frequently. + const { getStockNumber } = select( 'my-shop' ); + const { startSale } = dispatch( 'my-shop' ); + + return { + onClick() { + const dicountPercent = getStockNumber() > 50 ? 10 : 20; + startSale( discountPercent ); + }, + }; +} )( Button ); + // Rendered in the application: // // Start Sale! diff --git a/packages/data/src/components/with-dispatch/index.js b/packages/data/src/components/with-dispatch/index.js index 3c302c557187fa..6c5944085a6942 100644 --- a/packages/data/src/components/with-dispatch/index.js +++ b/packages/data/src/components/with-dispatch/index.js @@ -39,7 +39,7 @@ const withDispatch = ( mapDispatchToProps ) => createHigherOrderComponent( proxyDispatch( propName, ...args ) { // Original dispatcher is a pre-bound (dispatching) action creator. - mapDispatchToProps( this.props.registry.dispatch, this.props.ownProps )[ propName ]( ...args ); + mapDispatchToProps( this.props.registry.dispatch, this.props.ownProps, this.props.registry )[ propName ]( ...args ); } setProxyProps( props ) { @@ -49,8 +49,12 @@ const withDispatch = ( mapDispatchToProps ) => createHigherOrderComponent( // called, it is done only to determine the keys for which // proxy functions should be created. The actual registry // dispatch does not occur until the function is called. - const propsToDispatchers = mapDispatchToProps( this.props.registry.dispatch, props.ownProps ); + const propsToDispatchers = mapDispatchToProps( this.props.registry.dispatch, props.ownProps, this.props.registry ); this.proxyProps = mapValues( propsToDispatchers, ( dispatcher, propName ) => { + if ( typeof dispatcher !== 'function' ) { + // eslint-disable-next-line no-console + console.warn( `Property ${ propName } returned from mapDispatchToProps in withDispatch must be a function.` ); + } // Prebind with prop name so we have reference to the original // dispatcher to invoke. Track between re-renders to avoid // creating new function references every render. diff --git a/packages/data/src/components/with-dispatch/test/index.js b/packages/data/src/components/with-dispatch/test/index.js index b187c5b69410c4..c7a6fe9ac034f5 100644 --- a/packages/data/src/components/with-dispatch/test/index.js +++ b/packages/data/src/components/with-dispatch/test/index.js @@ -119,4 +119,67 @@ describe( 'withDispatch', () => { expect( firstRegistryAction ).toHaveBeenCalledTimes( 2 ); expect( secondRegistryAction ).toHaveBeenCalledTimes( 2 ); } ); + + it( 'always calls select with the latest state in the handler passed to the component', () => { + const store = registry.registerStore( 'counter', { + reducer: ( state = 0, action ) => { + if ( action.type === 'update' ) { + return action.count; + } + return state; + }, + actions: { + update: ( count ) => ( { type: 'update', count } ), + }, + selectors: { + getCount: ( state ) => state, + }, + } ); + + const Component = withDispatch( ( _dispatch, ownProps, { select: _select } ) => { + const outerCount = _select( 'counter' ).getCount(); + return { + update: () => { + const innerCount = _select( 'counter' ).getCount(); + expect( innerCount ).toBe( outerCount ); + const actionReturnedFromDispatch = _dispatch( 'counter' ).update( innerCount + 1 ); + expect( actionReturnedFromDispatch ).toBe( undefined ); + }, + }; + } )( ( props ) =>