From 8034516a0bfbd9f4f225fe301505361a3d0791eb Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Mon, 3 Dec 2018 19:46:34 +0100 Subject: [PATCH 01/15] Make a simple version of DefaultBlockAppender for mobile (#12434) * Make a simple version of DefaultBlockAppender for mobile * Use the same padding used for other blocks in DefaultBlockAppender * Update copy, auto focus and bind keypress * Do not bind key events * Change style of placeholder --- .../default-block-appender/index.native.js | 75 +++++++++++++++++++ .../default-block-appender/style.native.scss | 14 ++++ .../editor/src/components/index.native.js | 1 + 3 files changed, 90 insertions(+) create mode 100644 packages/editor/src/components/default-block-appender/index.native.js create mode 100644 packages/editor/src/components/default-block-appender/style.native.scss diff --git a/packages/editor/src/components/default-block-appender/index.native.js b/packages/editor/src/components/default-block-appender/index.native.js new file mode 100644 index 0000000000000..436ecc5772b00 --- /dev/null +++ b/packages/editor/src/components/default-block-appender/index.native.js @@ -0,0 +1,75 @@ +/** + * External dependencies + */ +import { TextInput, TouchableWithoutFeedback, View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { compose } from '@wordpress/compose'; +import { decodeEntities } from '@wordpress/html-entities'; +import { withSelect, withDispatch } from '@wordpress/data'; + +import styles from './style.scss'; + +export function DefaultBlockAppender( { + isLocked, + isVisible, + onAppend, + placeholder, +} ) { + if ( isLocked || ! isVisible ) { + return null; + } + + const value = decodeEntities( placeholder ) || __( 'Start writing or press \u2295 to add content' ); + + return ( + + + + + + + + ); +} + +export default compose( + withSelect( ( select, ownProps ) => { + const { getBlockCount, getEditorSettings, getTemplateLock } = select( 'core/editor' ); + + const isEmpty = ! getBlockCount( ownProps.rootClientId ); + const { bodyPlaceholder } = getEditorSettings(); + + return { + isVisible: isEmpty, + isLocked: !! getTemplateLock( ownProps.rootClientId ), + placeholder: bodyPlaceholder, + }; + } ), + withDispatch( ( dispatch, ownProps ) => { + const { + insertDefaultBlock, + startTyping, + } = dispatch( 'core/editor' ); + + return { + onAppend() { + const { rootClientId } = ownProps; + + insertDefaultBlock( undefined, rootClientId ); + startTyping(); + }, + }; + } ), +)( DefaultBlockAppender ); diff --git a/packages/editor/src/components/default-block-appender/style.native.scss b/packages/editor/src/components/default-block-appender/style.native.scss new file mode 100644 index 0000000000000..cc08f6c820ca9 --- /dev/null +++ b/packages/editor/src/components/default-block-appender/style.native.scss @@ -0,0 +1,14 @@ + +.blockHolder { + flex: 1 1 auto; +} + +.blockContainer { + background-color: $white; + padding: 8px; +} + +.textView { + color: #87a6bc; + font-size: 16px; +} diff --git a/packages/editor/src/components/index.native.js b/packages/editor/src/components/index.native.js index dc651f9e7e0a6..229efa5879cc3 100644 --- a/packages/editor/src/components/index.native.js +++ b/packages/editor/src/components/index.native.js @@ -6,5 +6,6 @@ export { default as MediaPlaceholder } from './media-placeholder'; export { default as BlockFormatControls } from './block-format-controls'; export { default as BlockControls } from './block-controls'; export { default as BlockEdit } from './block-edit'; +export { default as DefaultBlockAppender } from './default-block-appender'; export { default as EditorHistoryRedo } from './editor-history/redo'; export { default as EditorHistoryUndo } from './editor-history/undo'; From d5a8767b91f7a709f0667def8cf548cfcb44aa62 Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler Date: Tue, 4 Dec 2018 10:28:52 +0100 Subject: [PATCH 02/15] Stop using classname-to-style autotransform in react native (#12552) --- packages/block-library/src/more/edit.native.js | 10 +++++----- packages/block-library/src/nextpage/edit.native.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index 5b85da44359d5..a56a217e39c90 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -21,11 +21,11 @@ export default function MoreEdit( props ) { const value = customText !== undefined ? customText : defaultText; return ( - - - <!-- + + + <!-- - <Text className={ styles[ 'block-library-more__right-marker' ] }>--&gt;</Text> + <Text style={ styles[ 'block-library-more__right-marker' ] }>--&gt;</Text> </View> </View> ); } diff --git a/packages/block-library/src/nextpage/edit.native.js b/packages/block-library/src/nextpage/edit.native.js index 03cd3bfe3c7d1..413fda53fe217 100644 --- a/packages/block-library/src/nextpage/edit.native.js +++ b/packages/block-library/src/nextpage/edit.native.js @@ -18,7 +18,7 @@ export default function NextPageEdit( { attributes } ) { const { customText = __( 'Page break' ) } = attributes; return ( - <View className={ styles[ 'block-library-nextpage__container' ] }> + <View style={ styles[ 'block-library-nextpage__container' ] }> <Hr text={ customText } textStyle={ styles[ 'block-library-nextpage__text' ] } lineStyle={ styles[ 'block-library-nextpage__line' ] } /> From d442d65e7ca59b00e62d3f1d5f48c8a0cc509da3 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Wed, 5 Dec 2018 19:39:29 +0100 Subject: [PATCH 03/15] [RNMobile] Fix crash editing More blocks (#12620) * Use onChange instead of onChangeText in PlainText updates. * Convert the More block to Component, and re-use the same logic of the web when the field is empty. * Remove unsed variable, and format code * Move `);` to a new line --- .../block-library/src/more/edit.native.js | 66 ++++++++++++------- .../src/components/plain-text/index.native.js | 6 +- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index a56a217e39c90..36dc0495f8c8d 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -7,6 +7,7 @@ import { View, Text } from 'react-native'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; +import { Component } from '@wordpress/element'; /** * Internal dependencies @@ -14,28 +15,49 @@ import { __ } from '@wordpress/i18n'; import { PlainText } from '@wordpress/editor'; import styles from './editor.scss'; -export default function MoreEdit( props ) { - const { attributes, setAttributes, onFocus, onBlur } = props; - const { customText } = attributes; - const defaultText = __( 'Read more' ); - const value = customText !== undefined ? customText : defaultText; +export default class MoreEdit extends Component { + constructor() { + super( ...arguments ); + this.onChangeInput = this.onChangeInput.bind( this ); - return ( - <View style={ styles[ 'block-library-more__container' ] }> - <View style={ styles[ 'block-library-more__sub-container' ] }> - <Text style={ styles[ 'block-library-more__left-marker' ] }>&lt;!--</Text> - <PlainText - style={ styles[ 'block-library-more__plain-text' ] } - value={ value } - multiline={ true } - underlineColorAndroid="transparent" - onChange={ ( newValue ) => setAttributes( { customText: newValue } ) } - placeholder={ defaultText } - isSelected={ props.isSelected } - onFocus={ onFocus } - onBlur={ onBlur } - /> - <Text style={ styles[ 'block-library-more__right-marker' ] }>--&gt;</Text> + this.state = { + defaultText: __( 'Read more' ), + }; + } + + onChangeInput( newValue ) { + // Set defaultText to an empty string, allowing the user to clear/replace the input field's text + this.setState( { + defaultText: '', + } ); + const value = newValue.length === 0 ? undefined : newValue; + this.props.setAttributes( { customText: value } ); + } + + render() { + const { attributes, onFocus, onBlur } = this.props; + const { customText } = attributes; + const defaultText = __( 'Read more' ); + const value = customText !== undefined ? customText : defaultText; + + return ( + <View style={ styles[ 'block-library-more__container' ] }> + <View style={ styles[ 'block-library-more__sub-container' ] }> + <Text style={ styles[ 'block-library-more__left-marker' ] }>&lt;!--</Text> + <PlainText + style={ styles[ 'block-library-more__plain-text' ] } + value={ value } + multiline={ true } + underlineColorAndroid="transparent" + onChange={ this.onChangeInput } + placeholder={ defaultText } + isSelected={ this.props.isSelected } + onFocus={ onFocus } + onBlur={ onBlur } + /> + <Text style={ styles[ 'block-library-more__right-marker' ] }>--&gt;</Text> + </View> </View> - </View> ); + ); + } } diff --git a/packages/editor/src/components/plain-text/index.native.js b/packages/editor/src/components/plain-text/index.native.js index e94f1939040f5..f7b923b5b976f 100644 --- a/packages/editor/src/components/plain-text/index.native.js +++ b/packages/editor/src/components/plain-text/index.native.js @@ -28,12 +28,14 @@ export default class PlainText extends Component { render() { return ( <TextInput + { ...this.props } ref={ ( x ) => this._input = x } className={ [ styles[ 'editor-plain-text' ], this.props.className ] } - onChangeText={ ( text ) => this.props.onChange( text ) } + onChange={ ( event ) => { + this.props.onChange( event.nativeEvent.text ); + } } onFocus={ this.props.onFocus } // always assign onFocus as a props onBlur={ this.props.onBlur } // always assign onBlur as a props - { ...this.props } /> ); } From 5ca5479989f38f07fefdfe7cd679156d9b9b23cf Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler <dekervit@gmail.com> Date: Thu, 6 Dec 2018 10:28:30 +0100 Subject: [PATCH 04/15] Fix SVG styles for mobile (#12608) * Fix SVG styles for mobile * Simplify condition since className is always a string --- .../components/src/primitives/svg/index.native.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/components/src/primitives/svg/index.native.js b/packages/components/src/primitives/svg/index.native.js index 47c49b1bb6128..b0272e6b5a7b9 100644 --- a/packages/components/src/primitives/svg/index.native.js +++ b/packages/components/src/primitives/svg/index.native.js @@ -17,16 +17,8 @@ export { } from 'react-native-svg'; export const SVG = ( props ) => { - // We're using the react-native-classname-to-style plugin, so when a `className` prop is passed it gets converted to `style` here. - // Given it carries a string (as it was originally className) but an object is expected for `style`, - // we need to check whether `style` exists and is a string, and convert it to an object - - let styleValues = {}; - if ( typeof props.style === 'string' ) { - const oneStyle = props.style.split( ' ' ).map( ( element ) => styles[ element ] ).filter( Boolean ); - styleValues = Object.assign( styleValues, ...oneStyle ); - } - + const stylesFromClasses = ( props.className || '' ).split( ' ' ).map( ( element ) => styles[ element ] ).filter( Boolean ); + const styleValues = Object.assign( {}, props.style, ...stylesFromClasses ); const safeProps = { ...props, style: styleValues }; return ( From b9e9afdfff2c6cbc0509f4fef1295770b4378e88 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Thu, 6 Dec 2018 20:29:27 +0100 Subject: [PATCH 05/15] Check if Enter.key is the last inserted character, and add a new default block after the current More block. (#12639) Note: This is detected after the fact, and the newline could be visible on the block for a very small time. This is OK for the alpha, we will revisit the logic later. See https://github.com/wordpress-mobile/gutenberg-mobile/issues/324 --- packages/block-library/src/more/edit.native.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index 36dc0495f8c8d..a6845e2ade8a5 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -8,6 +8,10 @@ import { View, Text } from 'react-native'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; +import { + getDefaultBlockName, + createBlock, +} from '@wordpress/blocks'; /** * Internal dependencies @@ -26,6 +30,15 @@ export default class MoreEdit extends Component { } onChangeInput( newValue ) { + // Detect Enter.key and add new empty block after. + // Note: This is detected after the fact, and the newline could be visible on the block + // for a very small time. This is OK for the alpha, we will revisit the logic later. + // See https://github.com/wordpress-mobile/gutenberg-mobile/issues/324 + if ( newValue.indexOf( '\n' ) !== -1 ) { + const { insertBlocksAfter } = this.props; + insertBlocksAfter( [ createBlock( getDefaultBlockName() ) ] ); + return; + } // Set defaultText to an empty string, allowing the user to clear/replace the input field's text this.setState( { defaultText: '', From fa5cff95040c40a380ef2c516440abd8f476f020 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Tue, 11 Dec 2018 11:43:35 +0100 Subject: [PATCH 06/15] Call blur when deselecting RichText or PlainText component. (#12765) This is required to hide the keyboard when the focus moves to a non textual block. --- packages/editor/src/components/plain-text/index.native.js | 6 ++++++ packages/editor/src/components/rich-text/index.native.js | 2 ++ 2 files changed, 8 insertions(+) diff --git a/packages/editor/src/components/plain-text/index.native.js b/packages/editor/src/components/plain-text/index.native.js index f7b923b5b976f..2373f85ea5815 100644 --- a/packages/editor/src/components/plain-text/index.native.js +++ b/packages/editor/src/components/plain-text/index.native.js @@ -21,6 +21,12 @@ export default class PlainText extends Component { } } + componentDidUpdate( prevProps ) { + if ( ! this.props.isSelected && prevProps.isSelected ) { + this._input.blur(); + } + } + focus() { this._input.focus(); } diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js index 362dc4b359d1a..d909063d832a7 100644 --- a/packages/editor/src/components/rich-text/index.native.js +++ b/packages/editor/src/components/rich-text/index.native.js @@ -292,6 +292,8 @@ export class RichText extends Component { componentDidUpdate( prevProps ) { if ( this.props.isSelected && ! prevProps.isSelected ) { this._editor.focus(); + } else if ( ! this.props.isSelected && prevProps.isSelected ) { + this._editor.blur(); } } From 5821ec78a1f2286c5c536b74914d450fc32b2605 Mon Sep 17 00:00:00 2001 From: Pinar Olguc <pinarolguc@gmail.com> Date: Tue, 11 Dec 2018 16:30:35 +0300 Subject: [PATCH 07/15] Merge master into mobile (#12796) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * RichText: fix onSetup doc (#12607) * Update broken links (#12660) * Update broken links There was 2 broken links. I changed same way with Data Module Reference page. * Update docs/designers-developers/developers/block-api/README.md Co-Authored-By: cagdasdag <cagdasdag81@gmail.com> * Update docs/designers-developers/developers/block-api/README.md Co-Authored-By: cagdasdag <cagdasdag81@gmail.com> * Docs: Update Glossary (#12479) * Update glossar with missing terms * Convert glossary to use dl/dt/dd dtags. Fixes #9976 * Fix TinyMCE link * remove spacing around tags * Add template definition, with link * Updates per review * Update docs/designers-developers/glossary.md Co-Authored-By: mkaz <marcus@mkaz.com> * Add documentation for `safeDecodeURI()` and `filterURLForDisplay()` (#12570) * Add documentation for `safeDecodeURI()` and `filterURLForDisplay()` * Whitespace * Consistant Capit… i mean capitalization * Oxford comma Co-Authored-By: georgeh <george@hotelling.net> * Update theme-support.md (#12661) * Fix: Undoing Image Selection in Image Block results in Broken Image (#12567) * Optimize isViewportMatch (#12542) * Cache createBlock call in isUnmodifiedDefaultBlock (#12521) * Cache createBlock call in isUnmodifiedDefaultBlock * Invalidate cache when default block name changes * Merge ifs * Font Size Picker: Use a menuitemradio role and better labels. (#12372) * Use a menuitemradio role and better labels. * Restore Button and remove MenuItem import. * Use template literals. * Set document title for preview interstitial (#12466) * Fix e2e tests after the WordPress 5.0 upgrade (#12715) * Fix e2e tests after the WordPress 5.0 upgrade * Remove the php unit tests testing the WP5.0 core instead of the plugin * Meta Boxes: Don't hide disabled meta boxes by modifying DOM (#12628) Hiding disabled meta boxes by setting `element.style.display = 'none'` interferes with plugins like ACF which rely on being able to show and hide meta boxes using `$.hide()` and `$.show()`. Hiding the meta box using a new `.edit-post-meta-boxes-area .is-hidden` class ensures that we don't interfere with third party code. * Add a word-wrap style to the facebook embed preview screen (#11890) * Add a word-break style to the facebook embed preview screen to prevent the long embed url from breaking the block boundary * Fix typo and missing space in scss comment * Adding @aldavigdis to the contributors list (#12686) * Make media & text block placeholder translatable (#12706) * Fix: Problems on Media & Text block resizing; Load wp-block-library styles before wp-edit-blocks. (#12619) * Get wordcount type from translation (#12586) * get wordcount type from translation * Add description to explain the options for wordcount type * Added mylsef into contributors.md. :) * Only render InserterWithShortcuts on hover (#12510) * Fix issue where default appender has icons overlaying the text (#12536) * Fix issue where default appender has icons overlaying the text This fixes #11425. It adds padding to the right of the default block appender to fit 3 icons. * chore: Tweak spelling * Classic Block: set correct focus back after blur (#12415) * Classic Block: set correct focus back after blur * Add e2e test * reset bookmark on mousedown and touchstart * e2e: Look for aria-label="Add Media" rather than "Insert Media" * RichText: only replace range and nodes if different (#12547) * RichText: only set range if different * Check rangeCount * Also compare nodes * Add e2e test * Simplify * RichText: Document isRangeEqual * Testing: RichText: Assure subscriber removal * Unsubscribe in page.evaluate * Mark temporary eslint-config package as private (#12734) * When a post is saved, check for tinymce and save any editors. (#12568) * When a post is saved, check for tinymce and save any editors. * Importing tinymce and using tinyMCE vs the object stored in window.tinymce. * Updated version number and changelog. * no longer importing tinymce since we use the tinyMCE global. tinyMCE.triggerSave works now. checking if tinyMCE exists before making the call just in case. * Using typeof to check for tinyMCE and fixed issues brought up in travis run. * using window.tinyMCE again to avoid warning RE undefined var * Restore the package.json version. * Add e2e tests for the custom wp_editor metaboxes * Rename functions, removing gutenberg_ prefix (#12326) * Rename functions, removing gutenberg_ and prefixing with wp_ * Remove wp_ prefix to match core * Remove function check per review * Annotations: Apply annotation className as string (#12741) * RichText: Ensure instance is selected before setting back selection (#12737) * Fix for #11663 (#12728) * Fixed Deleting an HTML Anchor attribute leaves an empty HTML id attribute * Fixed Deleting an HTML Anchor attribute leaves an empty * Update plugin version to 4.7.0-rc.1 (#12752) * Add an error state to the image block to allow upload errors to display (#10224) * Try: JS Console warning for when in Quirks Mode (#12575) * Try: JS Console warning for when in Quirks Mode This PR detects whether the browser is in Quirks Mode. Quirks Mode is a rendering method used when the doctype definition is missing or incorrectly placed in the HTML source, causing the browser to have difficulty detecting the type of document it is to render. This is usually caused by a PHP error, or even just a style tag that is output incorrectly on the page. See discussion in https://github.com/WordPress/gutenberg/pull/12455 and https://github.com/WordPress/gutenberg/issues/11378. The usual result is Gutenberg rendering incorrectly, notably with metaboxes overlapping content. The purpose of this PR is to help developers debug the issue and fix it at the root. As such, it adds a console warning, props @nickcernis for the text: ``` [Warning] Your browser is using Quirks Mode. This can cause rendering issues such as blocks overlaying meta boxes in the editor. Quirks Mode can be triggered by PHP errors or HTML code appearing before the opening <!DOCTYPE html>. Try checking the raw page source or your site's PHP error log and resolving errors there, removing any HTML before the doctype, or disabling plugins. ``` It also augments the documentation to add a note about this. * Move warning to index.js * Remove try/catch. * Tweak: Remove redundant [warning] in warn call * Organizing screenshot assets for the block tutorial inside the designers-developers directory in the repo (#12745) * Rename backwards compatiblity to backward compatibility (#12751) * Rename backwards compatiblity to backward compatibility * Remove package-lock from commit * Update CONTRIBUTING.md Co-Authored-By: mkaz <marcus@mkaz.com> * Update CONTRIBUTING.md Co-Authored-By: mkaz <marcus@mkaz.com> * Whitespace in manifest * Update node-sass to 4.11.0 to support Node.js 11 (#12541) ## Description Fixes #12539 by updating node-sass to support Node.js 11. ## How has this been tested? Running `npm install` on macOS 10.14 with Node.js 11.2 without problems. ## Types of changes Minor dependency bump to support Node.js 11. ## Checklist: - [x] My code is tested. - [x] My code follows the WordPress code style. <!-- Check code: `npm run lint`, Guidelines: https://make.wordpress.org/core/handbook/best-practices/coding-standards/javascript/ --> - [x] My code follows the accessibility standards. <!-- Guidelines: https://make.wordpress.org/core/handbook/best-practices/coding-standards/accessibility-coding-standards/ --> - [x] My code has proper inline documentation. <!-- Guidelines: https://make.wordpress.org/core/handbook/best-practices/inline-documentation-standards/javascript/ --> --- CONTRIBUTING.md | 6 +- CONTRIBUTORS.md | 2 + README.md | 4 +- .../block-tutorial => assets}/inspector.png | Bin .../assets/toolbar-text.png | Bin 0 -> 22683 bytes .../designers-developers/developers/README.md | 2 +- .../backward-compatibility/README.md | 1 + .../deprecations.md | 2 +- .../meta-box.md | 8 +- .../backwards-compatibility/README.md | 1 - .../developers/block-api/README.md | 4 +- .../developers/themes/theme-support.md | 2 +- .../block-controls-toolbars-and-inspector.md | 4 +- docs/designers-developers/glossary.md | 78 ++- docs/manifest.json | 14 +- docs/toc.json | 6 +- gutenberg.php | 2 +- lib/client-assets.php | 3 +- lib/load.php | 5 +- package-lock.json | 160 ++++-- package.json | 4 +- packages/annotations/src/block/index.js | 2 +- packages/block-library/src/classic/edit.js | 14 + packages/block-library/src/embed/editor.scss | 5 + packages/block-library/src/image/edit.js | 8 +- .../src/latest-comments/index.php | 58 +- packages/block-library/src/media-text/edit.js | 4 +- .../block-library/src/paragraph/editor.scss | 9 +- packages/blocks/src/api/test/utils.js | 29 + packages/blocks/src/api/utils.js | 11 +- .../components/src/font-size-picker/index.js | 45 +- .../src/server-side-render/README.md | 4 +- packages/edit-post/CHANGELOG.md | 5 + .../meta-boxes/meta-box-visibility.js | 11 +- .../meta-boxes/meta-boxes-area/style.scss | 6 + packages/edit-post/src/index.js | 7 + packages/edit-post/src/store/effects.js | 7 +- .../default-block-appender/index.js | 14 +- .../test/__snapshots__/index.js.snap | 9 +- .../components/post-preview-button/index.js | 1 + .../editor/src/components/rich-text/index.js | 16 +- .../editor/src/components/word-count/index.js | 10 +- packages/editor/src/hooks/anchor.js | 2 +- packages/editor/src/hooks/test/anchor.js | 12 + packages/eslint-config/package.json | 1 + packages/rich-text/src/to-dom.js | 46 +- packages/url/README.md | 16 + packages/viewport/src/store/selectors.js | 14 +- phpunit/class-parsing-test.php | 105 ---- ...ss-rest-block-renderer-controller-test.php | 454 --------------- phpunit/class-rest-blocks-controller-test.php | 204 ------- phpunit/class-rest-search-controller-test.php | 521 ------------------ .../__snapshots__/rich-text.test.js.snap | 6 + .../wp-editor-meta-box.test.js.snap | 3 + .../blocks/__snapshots__/classic.test.js.snap | 3 + test/e2e/specs/blocks/classic.test.js | 73 +++ test/e2e/specs/classic-editor.test.js | 25 - test/e2e/specs/font-size-picker.test.js | 12 +- test/e2e/specs/new-post.test.js | 7 + test/e2e/specs/rich-text.test.js | 71 +++ test/e2e/specs/templates.test.js | 10 +- test/e2e/specs/wp-editor-meta-box.test.js | 38 ++ test/e2e/specs/writing-flow.test.js | 3 + test/e2e/support/plugins.js | 2 +- test/e2e/test-plugins/post-formats.php | 15 + test/e2e/test-plugins/wp-editor-metabox.php | 27 + 66 files changed, 741 insertions(+), 1512 deletions(-) rename docs/designers-developers/{developers/tutorials/block-tutorial => assets}/inspector.png (100%) create mode 100644 docs/designers-developers/assets/toolbar-text.png create mode 100644 docs/designers-developers/developers/backward-compatibility/README.md rename docs/designers-developers/developers/{backwards-compatibility => backward-compatibility}/deprecations.md (97%) rename docs/designers-developers/developers/{backwards-compatibility => backward-compatibility}/meta-box.md (85%) delete mode 100644 docs/designers-developers/developers/backwards-compatibility/README.md delete mode 100644 phpunit/class-parsing-test.php delete mode 100644 phpunit/class-rest-block-renderer-controller-test.php delete mode 100644 phpunit/class-rest-blocks-controller-test.php delete mode 100644 phpunit/class-rest-search-controller-test.php create mode 100644 test/e2e/specs/__snapshots__/wp-editor-meta-box.test.js.snap create mode 100644 test/e2e/specs/blocks/__snapshots__/classic.test.js.snap create mode 100644 test/e2e/specs/blocks/classic.test.js delete mode 100644 test/e2e/specs/classic-editor.test.js create mode 100644 test/e2e/specs/wp-editor-meta-box.test.js create mode 100644 test/e2e/test-plugins/post-formats.php create mode 100644 test/e2e/test-plugins/wp-editor-metabox.php diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ccb59aa2df44..e9a47c7322e47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -166,9 +166,9 @@ Maintaining dozens of npm packages is difficult—it can be tough to keep track The developer who proposes a change (pull request) is responsible to choose the correct version increment (`major`, `minor`, or `patch`) according to the following guidelines: -- Major version X (X.y.z | X > 0) should be changed with any backwards-incompatible/"breaking" change. This will usually occur at the final stage of deprecating and removing of a feature. -- Minor version Y (x.Y.z | x > 0) should be changed when you add functionality or change functionality in a backwards-compatible manner. It must be incremented if any public API functionality is marked as deprecated. -- Patch version Z (x.y.Z | x > 0) should be incremented when you make backwards-compatible bug fixes. +- Major version X (X.y.z | X > 0) should be changed with any backward incompatible/"breaking" change. This will usually occur at the final stage of deprecating and removing of a feature. +- Minor version Y (x.Y.z | x > 0) should be changed when you add functionality or change functionality in a backward compatible manner. It must be incremented if any public API functionality is marked as deprecated. +- Patch version Z (x.y.Z | x > 0) should be incremented when you make backward compatible bug fixes. When in doubt, refer to [Semantic Versioning specification](https://semver.org/). diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8f7b5d83932b6..8279df075b66b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -122,3 +122,5 @@ This list is manually curated to include valuable contributions by volunteers th | @sharazghouri | @sharaz | | @jakeparis | @jakeparis | | @designsimply | @designsimply | +| @aldavigdis | @aldavigdis | +| @miya0001 | @miyauchi | diff --git a/README.md b/README.md index 03251ceba2160..855958ef9635e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repo is the development hub for the <a href="https://make.wordpress.org/core/2017/01/04/focus-tech-and-design-leads/">editor focus in WordPress Core</a>. `Gutenberg` is the project name. ## Getting started -- **Download:** If you want to use the latest release with your WordPress site, <a href="https://wordpress.org/plugins/gutenberg/">download the latest release from the WordPress.org plugins repository</a>. +- **Download:** If you want to use the latest release with your WordPress site, <a href="https://wordpress.org/plugins/gutenberg/">download the latest release from the WordPress.org plugins repository</a>. - **Discuss:** Conversations and discussions take place in <a href="https://wordpress.slack.com/messages/C02QB2JS7">`#core-editor` channel on the Making WordPress Slack</a>. - **Contribute:** Development of Gutenberg happens in this GitHub repo. Get started by <a href="https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md">reading the contributing guidelines</a>. - **Learn:** <a href="https://wordpress.org/gutenberg/">Discover more about the project on WordPress.org</a>. @@ -44,7 +44,7 @@ Check out the <a href="https://github.com/WordPress/gutenberg/blob/master/docs/r ## Compatibility -Posts are backwards compatible, and shortcodes will still work. We are continuously exploring how highly-tailored meta boxes can be accommodated, and are looking at solutions ranging from a plugin to disable Gutenberg to automatically detecting whether to load Gutenberg or not. While we want to make sure the new editing experience from writing to publishing is user-friendly, we’re committed to finding a good solution for highly-tailored existing sites. +Posts are backward compatible, and shortcodes will still work. We are continuously exploring how highly-tailored meta boxes can be accommodated, and are looking at solutions ranging from a plugin to disable Gutenberg to automatically detecting whether to load Gutenberg or not. While we want to make sure the new editing experience from writing to publishing is user-friendly, we’re committed to finding a good solution for highly-tailored existing sites. ## The stages of Gutenberg diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/inspector.png b/docs/designers-developers/assets/inspector.png similarity index 100% rename from docs/designers-developers/developers/tutorials/block-tutorial/inspector.png rename to docs/designers-developers/assets/inspector.png diff --git a/docs/designers-developers/assets/toolbar-text.png b/docs/designers-developers/assets/toolbar-text.png new file mode 100644 index 0000000000000000000000000000000000000000..76b18c6b8f368f3abd5d2559e5a6f71c75913ed4 GIT binary patch literal 22683 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 literal 0 HcmV?d00001 diff --git a/docs/designers-developers/developers/README.md b/docs/designers-developers/developers/README.md index 5c552b2bd835e..8dbc72195d05c 100644 --- a/docs/designers-developers/developers/README.md +++ b/docs/designers-developers/developers/README.md @@ -26,7 +26,7 @@ You can also filter certain aspects of the editor; this is documented on the [Ed **Porting PHP meta boxes to blocks and Gutenberg plugins is highly encouraged!** -Discover how [Meta Box](../../../docs/designers-developers/developers/backwards-compatibility/meta-box.md) support works in Gutenberg. +Discover how [Meta Box](../../../docs/designers-developers/developers/backward-compatibility/meta-box.md) support works in Gutenberg. ## Theme Support diff --git a/docs/designers-developers/developers/backward-compatibility/README.md b/docs/designers-developers/developers/backward-compatibility/README.md new file mode 100644 index 0000000000000..bd559617c5b17 --- /dev/null +++ b/docs/designers-developers/developers/backward-compatibility/README.md @@ -0,0 +1 @@ +# Backward Compatibility diff --git a/docs/designers-developers/developers/backwards-compatibility/deprecations.md b/docs/designers-developers/developers/backward-compatibility/deprecations.md similarity index 97% rename from docs/designers-developers/developers/backwards-compatibility/deprecations.md rename to docs/designers-developers/developers/backward-compatibility/deprecations.md index 804f55c0a2c19..7b690e995c516 100644 --- a/docs/designers-developers/developers/backwards-compatibility/deprecations.md +++ b/docs/designers-developers/developers/backward-compatibility/deprecations.md @@ -1,6 +1,6 @@ # Deprecations -Gutenberg's deprecation policy is intended to support backwards-compatibility for releases, when possible. The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version. +Gutenberg's deprecation policy is intended to support backward compatibility for releases, when possible. The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version. ## 4.5.0 - `Dropdown.refresh()` has been deprecated as the contained `Popover` is now automatically refreshed. diff --git a/docs/designers-developers/developers/backwards-compatibility/meta-box.md b/docs/designers-developers/developers/backward-compatibility/meta-box.md similarity index 85% rename from docs/designers-developers/developers/backwards-compatibility/meta-box.md rename to docs/designers-developers/developers/backward-compatibility/meta-box.md index eb96bfc38e0ff..8e722b4cb3156 100644 --- a/docs/designers-developers/developers/backwards-compatibility/meta-box.md +++ b/docs/designers-developers/developers/backward-compatibility/meta-box.md @@ -21,7 +21,7 @@ WordPress will fall back to the Classic editor, where the meta box will continue Explicitly setting `__block_editor_compatible_meta_box` to `true` will cause WordPress to stay in Gutenberg (assuming another meta box doesn't cause a fallback). -After a meta box is converted to a block, it can be declared as existing for backwards compatibility: +After a meta box is converted to a block, it can be declared as existing for backward compatibility: ```php add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback', @@ -32,7 +32,7 @@ add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback', ); ``` -When Gutenberg is used, this meta box will no longer be displayed in the meta box area, as it now only exists for backwards compatibility purposes. It will continue to display correctly in the Classic editor, should some other meta box cause a fallback. +When Gutenberg is used, this meta box will no longer be displayed in the meta box area, as it now only exists for backward compatibility purposes. It will continue to display correctly in the Classic editor, should some other meta box cause a fallback. ### Meta Box Data Collection @@ -42,7 +42,7 @@ See `lib/register.php gutenberg_trick_plugins_into_registering_meta_boxes()` `gutenberg_collect_meta_box_data()` is hooked in later on `admin_head`. It will run through the functions and hooks that `post.php` runs to register meta boxes; namely `add_meta_boxes`, `add_meta_boxes_{$post->post_type}`, and `do_meta_boxes`. -A copy of the global `$wp_meta_boxes` is made then filtered through `apply_filters( 'filter_gutenberg_meta_boxes', $_meta_boxes_copy );`, which will strip out any core meta boxes, standard custom taxonomy meta boxes, and any meta boxes that have declared themselves as only existing for backwards compatibility purposes. +A copy of the global `$wp_meta_boxes` is made then filtered through `apply_filters( 'filter_gutenberg_meta_boxes', $_meta_boxes_copy );`, which will strip out any core meta boxes, standard custom taxonomy meta boxes, and any meta boxes that have declared themselves as only existing for backward compatibility purposes. Then each location for this particular type of meta box is checked for whether it is active. If it is not empty a value of true is stored, if it is empty a value of false is stored. This meta box location data is then dispatched by the editor Redux store in `INITIALIZE_META_BOX_STATE`. @@ -82,3 +82,5 @@ Most PHP meta boxes should continue to work in Gutenberg, but some meta boxes th - Plugins relying on selectors that target the post title, post content fields, and other metaboxes (of the old editor). - Plugins relying on TinyMCE's API because there's no longer a single TinyMCE instance to talk to in Gutenberg. - Plugins making updates to their DOM on "submit" or on "save". + +Please also note that if your plugin triggers a PHP warning or notice to be output on the page, this will cause the HTML document type (`<!DOCTYPE html>`) to be output incorrectly. This will cause the browser to render using "Quirks Mode", which is a compatibility layer that gets enabled when the browser doesn't know what type of document it is parsing. The block editor is not meant to work in this mode, but it can _appear_ to be working just fine. If you encounter issues such as *meta boxes overlaying the editor* or other layout issues, please check the raw page source of your document to see that the document type definition is the first thing output on the page. There will also be a warning in the JavaScript console, noting the issue. diff --git a/docs/designers-developers/developers/backwards-compatibility/README.md b/docs/designers-developers/developers/backwards-compatibility/README.md deleted file mode 100644 index bd453cba0e56a..0000000000000 --- a/docs/designers-developers/developers/backwards-compatibility/README.md +++ /dev/null @@ -1 +0,0 @@ -# Backwards Compatibility diff --git a/docs/designers-developers/developers/block-api/README.md b/docs/designers-developers/developers/block-api/README.md index 1301449ecdf2c..2c18a261d8fc5 100644 --- a/docs/designers-developers/developers/block-api/README.md +++ b/docs/designers-developers/developers/block-api/README.md @@ -4,8 +4,8 @@ Blocks are the fundamental element of the Gutenberg editor. They are the primary ## Registering a block -All blocks must be registered before they can be used in the editor. You can learn about block registration, and the available options, in the [block registration](block-api/block-registration.md) documentation. +All blocks must be registered before they can be used in the editor. You can learn about block registration, and the available options, in the [block registration](../../../../docs/designers-developers/developers/block-api/block-registration.md) documentation. ## Block `edit` and `save` -The `edit` and `save` functions define the editor interface with which a user would interact, and the markup to be serialized back when a post is saved. They are the heart of how a block operates, so they are [covered separately](block-api/block-edit-save.md). +The `edit` and `save` functions define the editor interface with which a user would interact, and the markup to be serialized back when a post is saved. They are the heart of how a block operates, so they are [covered separately](../../../../docs/designers-developers/developers/block-api/block-edit-save.md). diff --git a/docs/designers-developers/developers/themes/theme-support.md b/docs/designers-developers/developers/themes/theme-support.md index abc611dc5d000..a5da2c7199e97 100644 --- a/docs/designers-developers/developers/themes/theme-support.md +++ b/docs/designers-developers/developers/themes/theme-support.md @@ -4,7 +4,7 @@ The new Blocks include baseline support in all themes, enhancements to opt-in to There are a few new concepts to consider when building themes: -- **Editor Color Palette** - A default set of colors is provided, but themes and register their own and optionally lock users into picking from the defined palette. +- **Editor Color Palette** - A default set of colors is provided, but themes can register their own and optionally lock users into picking from the defined palette. - **Editor Text Size Palette** - A default set of sizes is provided, but themes and register their own and optionally lock users into picking from preselected sizes. - **Responsive Embeds** - Themes must opt-in to responsive embeds. - **Frontend & Editor Styles** - To get the most out of blocks, theme authors will want to make sure Core styles look good and opt-in, or write their own styles to best fit their theme. diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbars-and-inspector.md b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbars-and-inspector.md index de6509e4c42b7..1bad64b3d9532 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbars-and-inspector.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbars-and-inspector.md @@ -4,7 +4,7 @@ To simplify block customization and ensure a consistent experience for users, th ## Toolbar -<img src="https://cldup.com/jUslj672CK.png" width="391" height="79" alt="toolbar"> +![Screenshot of the rich text toolbar applied to a paragraph block inside the block editor](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/assets/toolbar-text.png) When the user selects a block, a number of control buttons may be shown in a toolbar above the selected block. Some of these block-level controls are included automatically if the editor is able to transform the block to another type, or if the focused element is an RichText component. @@ -171,7 +171,7 @@ Note that `BlockControls` is only visible when the block is currently selected a ## Inspector -<img src="https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/tutorials/block-tutorial/inspector.png" with="281" height="527" alt="inspector"> +![Screenshot of the inspector panel focused on the settings for a paragraph block](https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/assets/inspector.png) The inspector is used to display less-often-used settings or settings that require more screen space. The inspector should be used for **block-level settings only**. diff --git a/docs/designers-developers/glossary.md b/docs/designers-developers/glossary.md index afa574179db27..f51c0d16d5dba 100644 --- a/docs/designers-developers/glossary.md +++ b/docs/designers-developers/glossary.md @@ -1,21 +1,61 @@ # Glossary -- **Attribute sources**: An object describing the attributes shape of a block. The keys can be named as most appropriate to describe the state of a block type. The value for each key is a function which describes the strategy by which the attribute value should be extracted from the content of a saved post's content. When processed, a new object is created, taking the form of the keys defined in the attribute sources, where each value is the result of the attribute source function. -- **Attributes**: The object representation of the current state of a block in post content. When loading a saved post, this is determined by the attribute sources for the block type. These values can change over time during an editing session when the user modifies a block, and are used when determining how to serialize the block. -- **Block**: The abstract term used to describe units of markup that, composed together, form the content or layout of a webpage. The idea combines concepts of what in WordPress today we achieve with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. -- **Block Categories**: These are not a WordPress taxonomy, but instead used internally to sort blocks in the Block Inserter. -- **Block Inserter**: Primary interface for selecting from the available blocks, triggered by plus icon buttons on Blocks or in the top-left of the editor interface. -- **Block name**: A unique identifier for a block type, consisting of a plugin-specific namespace and a short label describing the block's intent. e.g. `core/image` -- **Block type**: In contrast with the blocks composing a particular post, a block type describes the blueprint by which any block of that type should behave. So while there may be many images within a post, each behaves consistent with a unified image block type definition. -- **Classic block**: -- **Dynamic block**: A type of block where the content of which may change and cannot be determined at the time of saving a post, instead calculated any time the post is shown on the front of a site. These blocks may save fallback content or no content at all in their JavaScript implementation, instead deferring to a PHP block implementation for runtime rendering. -- **RichText**: A common component enabling rich content editing including bold, italics, hyperlinks, etc. It is not too much unlike the single editor region of the legacy post editor, and is in fact powered by the same TinyMCE library. -- **Inspector**: A block settings region shown in place of the post settings when a block is selected. Fields may be shown here to allow the user to customize the selected block. -- **Post settings**: A sidebar region containing metadata fields for the post, including scheduling, visibility, terms, and featured image. -- **Reusable block**: -- **Sidebar**: -- **Serialization**: The process of converting a block's attributes object into HTML markup, typically occurring when saving the post. -- **Static block**: A type of block where the content of which is known at the time of saving a post. A static block will be saved with HTML markup directly in post content. -- **TinyMCE**: [TinyMCE](https://www.tinymce.com/) is a web-based JavaScript WYSIWYG (What You See Is What You Get) editor. -- **Toolbar**: A set of button controls. In the context of a block, usually referring to the toolbar of block controls shown above the selected block. -- **Template**: +<dl> +<dt>Attribute sources</dt> +<dd>An object describing the attributes shape of a block. The keys can be named as most appropriate to describe the state of a block type. The value for each key is a function which describes the strategy by which the attribute value should be extracted from the content of a saved post's content. When processed, a new object is created, taking the form of the keys defined in the attribute sources, where each value is the result of the attribute source function.</dd> + +<dt>Attributes</dt> +<dd>The object representation of the current state of a block in post content. When loading a saved post, this is determined by the attribute sources for the block type. These values can change over time during an editing session when the user modifies a block, and are used when determining how to serialize the block.</dd> + +<dt>Block</dt> +<dd>The abstract term used to describe units of markup that, composed together, form the content or layout of a webpage. The idea combines concepts of what in WordPress today we achieve with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience.</dd> + +<dt>Block Categories</dt> +<dd>These are not a WordPress taxonomy, but instead used internally to sort blocks in the Block Inserter.</dd> + +<dt>Block Inserter</dt> +<dd>Primary interface for selecting from the available blocks, triggered by plus icon buttons on Blocks or in the top-left of the editor interface.</dd> + +<dt>Block name</dt> +<dd>A unique identifier for a block type, consisting of a plugin-specific namespace and a short label describing the block's intent. e.g. <code>core/image</code></dd> + +<dt>Block type</dt> +<dd>In contrast with the blocks composing a particular post, a block type describes the blueprint by which any block of that type should behave. So while there may be many images within a post, each behaves consistent with a unified image block type definition.</dd> + +<dt>Classic block</dt> +<dd>A block which embeds the TinyMCE editor as a block, TinyMCE was the base of the previous core editor. Older content created prior to the block editor will be loaded in to a single Classic block.</dd> + +<dt>Dynamic block</dt> +<dd>A type of block where the content of which may change and cannot be determined at the time of saving a post, instead calculated any time the post is shown on the front of a site. These blocks may save fallback content or no content at all in their JavaScript implementation, instead deferring to a PHP block implementation for runtime rendering.</dd> + +<dt>Inspector</dt> +<dd>A block settings region shown in place of the post settings when a block is selected. Fields may be shown here to allow the user to customize the selected block.</dd> + +<dt>Post settings</dt> +<dd>A sidebar region containing metadata fields for the post, including scheduling, visibility, terms, and featured image.</dd> + +<dt>RichText</dt> +<dd>A common component enabling rich content editing including bold, italics, hyperlinks, etc. It is not too much unlike the single editor region of the legacy post editor, and is in fact powered by the same TinyMCE library.</dd> + +<dt>Reusable block</dt> +<dd>A block that is saved and then can be shared as a reusable, repeatable piece of content.</dd> + +<dt>Sidebar</dt> +<dd>The panel on the right which contains the document and block settings. The sidebar is toggled using the Settings gear icon.</dd> + +<dt>Serialization</dt> +<dd>The process of converting a block's attributes object into HTML markup, which occurs each time a block is edited.</dd> + +<dt>Static block</dt> +<dd>A type of block where the content of which is known at the time of saving a post. A static block will be saved with HTML markup directly in post content.</dd> + +<dt>TinyMCE</dt> +<dd><a href="https://www.tinymce.com/">TinyMCE</a> is a web-based JavaScript WYSIWYG (What You See Is What You Get) editor.</dd> + +<dt>Toolbar</dt> +<dd>A set of button controls. In the context of a block, usually referring to the toolbar of block controls shown above the selected block.</dd> + +<dt>Template</dt> +<dd> A template is a pre-defined arrangement of blocks, possibly with predefined attributes or placeholder content. You can provide a template for a post type, to give users a starting point when creating a new piece of content, or inside a custom block with the <code>InnerBlocks</code> component. See the templates documentation for more information. See <a href="../../developers/block-api/block-templates.md">templates documentation</a> for more information.</dd> + +</dl> diff --git a/docs/manifest.json b/docs/manifest.json index 2dae91884153a..e8925eec9fb9c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -132,22 +132,22 @@ "parent": "themes" }, { - "title": "Backwards Compatibility", - "slug": "backwards-compatibility", - "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/backwards-compatibility/README.md", + "title": "Backward Compatibility", + "slug": "backward-compatibility", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/backward-compatibility/README.md", "parent": "developers" }, { "title": "Deprecations", "slug": "deprecations", - "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/backwards-compatibility/deprecations.md", - "parent": "backwards-compatibility" + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/backward-compatibility/deprecations.md", + "parent": "backward-compatibility" }, { "title": "Meta Boxes", "slug": "meta-box", - "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/backwards-compatibility/meta-box.md", - "parent": "backwards-compatibility" + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/backward-compatibility/meta-box.md", + "parent": "backward-compatibility" }, { "title": "Tutorials", diff --git a/docs/toc.json b/docs/toc.json index 211c74631ec70..6dd5e876cbc00 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -24,9 +24,9 @@ {"docs/designers-developers/developers/themes/README.md": [ {"docs/designers-developers/developers/themes/theme-support.md": []} ]}, - {"docs/designers-developers/developers/backwards-compatibility/README.md": [ - {"docs/designers-developers/developers/backwards-compatibility/deprecations.md": []}, - {"docs/designers-developers/developers/backwards-compatibility/meta-box.md": []} + {"docs/designers-developers/developers/backward-compatibility/README.md": [ + {"docs/designers-developers/developers/backward-compatibility/deprecations.md": []}, + {"docs/designers-developers/developers/backward-compatibility/meta-box.md": []} ]}, {"docs/designers-developers/developers/tutorials/readme.md": [ {"docs/designers-developers/developers/tutorials/block-tutorial/readme.md" :[ diff --git a/gutenberg.php b/gutenberg.php index 2f75a595903e7..9f9d7b183aeb5 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.6.1 + * Version: 4.7.0-rc.1 * Author: Gutenberg Team * * @package gutenberg diff --git a/lib/client-assets.php b/lib/client-assets.php index 6fa221bf4c5d8..c1a6e847bba5e 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -391,7 +391,7 @@ function gutenberg_register_scripts_and_styles() { wp_add_inline_script( 'wp-block-library', $script, 'before' ); // Editor Styles. - // This empty stylesheet is defined to ensure backwards compatibility. + // This empty stylesheet is defined to ensure backward compatibility. gutenberg_override_style( 'wp-blocks', false ); $fonts_url = ''; @@ -461,6 +461,7 @@ function gutenberg_register_scripts_and_styles() { array( 'wp-components', 'wp-editor', + 'wp-block-library', // Always include visual styles so the editor never appears broken. 'wp-block-library-theme', ), diff --git a/lib/load.php b/lib/load.php index ee1c973f99568..0c55db3a37f6c 100644 --- a/lib/load.php +++ b/lib/load.php @@ -62,10 +62,7 @@ if ( ! function_exists( 'render_block_core_categories' ) ) { require dirname( __FILE__ ) . '/../packages/block-library/src/categories/index.php'; } -// Currently merged in core as `gutenberg_render_block_core_latest_comments`, -// expected to change soon. -if ( ! function_exists( 'render_block_core_latest_comments' ) - && ! function_exists( 'gutenberg_render_block_core_latest_comments' ) ) { +if ( ! function_exists( 'render_block_core_latest_comments' ) ) { require dirname( __FILE__ ) . '/../packages/block-library/src/latest-comments/index.php'; } if ( ! function_exists( 'render_block_core_latest_posts' ) ) { diff --git a/package-lock.json b/package-lock.json index df6980879a342..347cbcd533748 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.6.1", + "version": "4.7.0-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -15030,9 +15030,9 @@ } }, "node-sass": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz", - "integrity": "sha512-LdxoJLZutx0aQXHtWIYwJKMj+9pTjneTcLWJgzf2XbGu0q5pRNqW5QvFCEdm3mc5rJOdru/mzln5d0EZLacf6g==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", "dev": true, "requires": { "async-foreach": "^0.1.3", @@ -15048,14 +15048,26 @@ "meow": "^3.7.0", "mkdirp": "^0.5.1", "nan": "^2.10.0", - "node-gyp": "^3.3.1", + "node-gyp": "^3.8.0", "npmlog": "^4.0.0", - "request": "2.87.0", + "request": "^2.88.0", "sass-graph": "^2.2.4", "stdout-stream": "^1.4.0", "true-case-path": "^1.0.2" }, "dependencies": { + "ajv": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", + "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", + "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" + } + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -15068,6 +15080,12 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", @@ -15076,7 +15094,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -15086,7 +15104,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -15107,6 +15125,28 @@ "which": "^1.2.9" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "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 + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -15116,15 +15156,11 @@ "repeating": "^2.0.0" } }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "map-obj": { "version": "1.0.1", @@ -15134,7 +15170,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -15150,12 +15186,33 @@ "trim-newlines": "^1.0.0" } }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -15166,9 +15223,37 @@ "strip-indent": "^1.0.1" } }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -18883,7 +18968,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -18903,7 +18988,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -19038,7 +19123,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -19674,9 +19759,9 @@ "dev": true }, "stdout-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", - "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "dev": true, "requires": { "readable-stream": "^2.0.1" @@ -20723,27 +20808,12 @@ "dev": true }, "true-case-path": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", - "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "dev": true, "requires": { - "glob": "^6.0.4" - }, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "glob": "^7.1.2" } }, "tryer": { diff --git a/package.json b/package.json index 541ebbfa9df50..432662628f9e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.6.1", + "version": "4.7.0-rc.1", "private": true, "description": "A new WordPress editor experience", "repository": "git+https://github.com/WordPress/gutenberg.git", @@ -90,7 +90,7 @@ "lint-staged": "7.2.0", "lodash": "4.17.10", "mkdirp": "0.5.1", - "node-sass": "4.9.2", + "node-sass": "4.11.0", "path-type": "3.0.0", "pegjs": "0.10.0", "phpegjs": "1.0.0-beta7", diff --git a/packages/annotations/src/block/index.js b/packages/annotations/src/block/index.js index 5095fc473d67e..e2677bbf6a9b3 100644 --- a/packages/annotations/src/block/index.js +++ b/packages/annotations/src/block/index.js @@ -17,7 +17,7 @@ const addAnnotationClassName = ( OriginalComponent ) => { return { className: annotations.map( ( annotation ) => { return 'is-annotated-by-' + annotation.source; - } ), + } ).join( ' ' ), }; } )( OriginalComponent ); }; diff --git a/packages/block-library/src/classic/edit.js b/packages/block-library/src/classic/edit.js index 2502c0f7e0eb5..f0eb37ff30ca9 100644 --- a/packages/block-library/src/classic/edit.js +++ b/packages/block-library/src/classic/edit.js @@ -78,6 +78,7 @@ export default class ClassicEdit extends Component { onSetup( editor ) { const { attributes: { content }, setAttributes } = this.props; const { ref } = this; + let bookmark; this.editor = editor; @@ -86,12 +87,25 @@ export default class ClassicEdit extends Component { } editor.on( 'blur', () => { + bookmark = editor.selection.getBookmark( 2, true ); + setAttributes( { content: editor.getContent(), } ); + + editor.once( 'focus', () => { + if ( bookmark ) { + editor.selection.moveToBookmark( bookmark ); + } + } ); + return false; } ); + editor.on( 'mousedown touchstart', () => { + bookmark = null; + } ); + editor.on( 'keydown', ( event ) => { if ( ( event.keyCode === BACKSPACE || event.keyCode === DELETE ) && isTmceEmpty( editor ) ) { // delete the block diff --git a/packages/block-library/src/embed/editor.scss b/packages/block-library/src/embed/editor.scss index 0b79d5b0414be..29cbd9fb932cf 100644 --- a/packages/block-library/src/embed/editor.scss +++ b/packages/block-library/src/embed/editor.scss @@ -25,4 +25,9 @@ font-size: $default-font-size; } } + + // Stops long URLs from breaking out of the no preview available screen + .components-placeholder__error { + word-break: break-word; + } } diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index cb059b5bfcdc6..f7fdd73a9aaa5 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -121,7 +121,7 @@ class ImageEdit extends Component { } componentDidMount() { - const { attributes, setAttributes } = this.props; + const { attributes, setAttributes, noticeOperations } = this.props; const { id, url = '' } = attributes; if ( isTemporaryImage( id, url ) ) { @@ -134,6 +134,10 @@ class ImageEdit extends Component { setAttributes( pickRelevantMediaFiles( image ) ); }, allowedTypes: ALLOWED_MEDIA_TYPES, + onError: ( message ) => { + noticeOperations.createErrorNotice( message ); + this.setState( { isEditing: true } ); + }, } ); } } @@ -414,7 +418,7 @@ class ImageEdit extends Component { </BlockControls> ); - if ( isEditing ) { + if ( isEditing || ! url ) { const src = isExternal ? url : undefined; return ( <Fragment> diff --git a/packages/block-library/src/latest-comments/index.php b/packages/block-library/src/latest-comments/index.php index 29e17e9de5008..fcc17c3cd6a00 100644 --- a/packages/block-library/src/latest-comments/index.php +++ b/packages/block-library/src/latest-comments/index.php @@ -5,34 +5,32 @@ * @package WordPress */ -if ( ! function_exists( 'gutenberg_draft_or_post_title' ) ) { - /** - * Get the post title. - * - * The post title is fetched and if it is blank then a default string is - * returned. - * - * Copied from `wp-admin/includes/template.php`, but we can't include that - * file because: - * - * 1. It causes bugs with test fixture generation and strange Docker 255 error - * codes. - * 2. It's in the admin; ideally we *shouldn't* be including files from the - * admin for a block's output. It's a very small/simple function as well, - * so duplicating it isn't too terrible. - * - * @since 3.3.0 - * - * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. - * @return string The post title if set; "(no title)" if no title is set. - */ - function gutenberg_draft_or_post_title( $post = 0 ) { - $title = get_the_title( $post ); - if ( empty( $title ) ) { - $title = __( '(no title)' ); - } - return esc_html( $title ); +/** + * Get the post title. + * + * The post title is fetched and if it is blank then a default string is + * returned. + * + * Copied from `wp-admin/includes/template.php`, but we can't include that + * file because: + * + * 1. It causes bugs with test fixture generation and strange Docker 255 error + * codes. + * 2. It's in the admin; ideally we *shouldn't* be including files from the + * admin for a block's output. It's a very small/simple function as well, + * so duplicating it isn't too terrible. + * + * @since 3.3.0 + * + * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. + * @return string The post title if set; "(no title)" if no title is set. + */ +function wp_latest_comments_draft_or_post_title( $post = 0 ) { + $title = get_the_title( $post ); + if ( empty( $title ) ) { + $title = __( '(no title)' ); } + return esc_html( $title ); } /** @@ -42,7 +40,7 @@ function gutenberg_draft_or_post_title( $post = 0 ) { * * @return string Returns the post content with latest comments added. */ -function gutenberg_render_block_core_latest_comments( $attributes = array() ) { +function render_block_core_latest_comments( $attributes = array() ) { // This filter is documented in wp-includes/widgets/class-wp-widget-recent-comments.php. $comments = get_comments( apply_filters( @@ -94,7 +92,7 @@ function gutenberg_render_block_core_latest_comments( $attributes = array() ) { // `_draft_or_post_title` calls `esc_html()` so we don't need to wrap that call in // `esc_html`. - $post_title = '<a class="wp-block-latest-comments__comment-link" href="' . esc_url( get_comment_link( $comment ) ) . '">' . gutenberg_draft_or_post_title( $comment->comment_post_ID ) . '</a>'; + $post_title = '<a class="wp-block-latest-comments__comment-link" href="' . esc_url( get_comment_link( $comment ) ) . '">' . wp_latest_comments_draft_or_post_title( $comment->comment_post_ID ) . '</a>'; $list_items_markup .= sprintf( /* translators: 1: author name (inside <a> or <span> tag, based on if they have a URL), 2: post title related to this comment */ @@ -179,6 +177,6 @@ function gutenberg_render_block_core_latest_comments( $attributes = array() ) { 'enum' => array( 'center', 'left', 'right', 'wide', 'full', '' ), ), ), - 'render_callback' => 'gutenberg_render_block_core_latest_comments', + 'render_callback' => 'render_block_core_latest_comments', ) ); diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js index c1cabf248e57c..9f9b626f837fd 100644 --- a/packages/block-library/src/media-text/edit.js +++ b/packages/block-library/src/media-text/edit.js @@ -7,7 +7,7 @@ import { get } from 'lodash'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, _x } from '@wordpress/i18n'; import { BlockControls, InnerBlocks, @@ -32,7 +32,7 @@ import MediaContainer from './media-container'; */ const ALLOWED_BLOCKS = [ 'core/button', 'core/paragraph', 'core/heading', 'core/list' ]; const TEMPLATE = [ - [ 'core/paragraph', { fontSize: 'large', placeholder: 'Content…' } ], + [ 'core/paragraph', { fontSize: 'large', placeholder: _x( 'Content…', 'content placeholder' ) } ], ]; class MediaTextEdit extends Component { diff --git a/packages/block-library/src/paragraph/editor.scss b/packages/block-library/src/paragraph/editor.scss index 98ea5e4e2b85a..1110e5d5832d4 100644 --- a/packages/block-library/src/paragraph/editor.scss +++ b/packages/block-library/src/paragraph/editor.scss @@ -1,6 +1,11 @@ // Specific to the empty paragraph placeholder: -// when shown on mobile and in nested contexts, the plus to add blocks shows up on the right. +// when shown on mobile and in nested contexts, one or more icons show up on the right. // This padding makes sure it doesn't overlap text. .editor-rich-text__tinymce[data-is-placeholder-visible="true"] + .editor-rich-text__tinymce.wp-block-paragraph { - padding-right: $icon-button-size; + padding-right: $icon-button-size * 3; + + // In nested contexts only one icon shows up. + .wp-block .wp-block & { + padding-right: $icon-button-size; + } } diff --git a/packages/blocks/src/api/test/utils.js b/packages/blocks/src/api/test/utils.js index 0bfeb8b0fe258..ea3fe57908441 100644 --- a/packages/blocks/src/api/test/utils.js +++ b/packages/blocks/src/api/test/utils.js @@ -67,5 +67,34 @@ describe( 'block helpers', () => { expect( isUnmodifiedDefaultBlock( block ) ).toBe( false ); } ); + + it( 'should invalidate cache if the default block name changed', () => { + registerBlockType( 'core/test-block1', { + attributes: { + includesDefault1: { + type: 'boolean', + default: true, + }, + }, + save: noop, + category: 'common', + title: 'test block', + } ); + registerBlockType( 'core/test-block2', { + attributes: { + includesDefault2: { + type: 'boolean', + default: true, + }, + }, + save: noop, + category: 'common', + title: 'test block', + } ); + setDefaultBlockName( 'core/test-block1' ); + isUnmodifiedDefaultBlock( createBlock( 'core/test-block1' ) ); + setDefaultBlockName( 'core/test-block2' ); + expect( isUnmodifiedDefaultBlock( createBlock( 'core/test-block2' ) ) ).toBe( true ); + } ); } ); } ); diff --git a/packages/blocks/src/api/utils.js b/packages/blocks/src/api/utils.js index 392593b440096..6dcabbcbcea01 100644 --- a/packages/blocks/src/api/utils.js +++ b/packages/blocks/src/api/utils.js @@ -38,7 +38,16 @@ export function isUnmodifiedDefaultBlock( block ) { return false; } - const newDefaultBlock = createBlock( defaultBlockName ); + // Cache a created default block if no cache exists or the default block + // name changed. + if ( + ! isUnmodifiedDefaultBlock.block || + isUnmodifiedDefaultBlock.block.name !== defaultBlockName + ) { + isUnmodifiedDefaultBlock.block = createBlock( defaultBlockName ); + } + + const newDefaultBlock = isUnmodifiedDefaultBlock.block; const blockType = getBlockType( defaultBlockName ); return every( blockType.attributes, ( value, key ) => diff --git a/packages/components/src/font-size-picker/index.js b/packages/components/src/font-size-picker/index.js index 16329316d5ad1..2908b3879c1eb 100644 --- a/packages/components/src/font-size-picker/index.js +++ b/packages/components/src/font-size-picker/index.js @@ -6,8 +6,7 @@ import { map } from 'lodash'; /** * WordPress dependencies */ -import { __, _x } from '@wordpress/i18n'; -import { withInstanceId } from '@wordpress/compose'; +import { __, _x, sprintf } from '@wordpress/i18n'; /** * Internal dependencies @@ -37,6 +36,7 @@ function FontSizePicker( { }; const currentFont = fontSizes.find( ( font ) => font.size === value ); + const currentFontSizeName = ( currentFont && currentFont.name ) || ( ! value && _x( 'Normal', 'font size name' ) ) || _x( 'Custom', 'font size name' ); return ( <BaseControl label={ __( 'Font Size' ) }> @@ -51,26 +51,34 @@ function FontSizePicker( { isLarge onClick={ onToggle } aria-expanded={ isOpen } - aria-label={ __( 'Custom font size' ) } + aria-label={ sprintf( + /* translators: %s: font size name */ + __( 'Font size: %s' ), currentFontSizeName + ) } > - { ( currentFont && currentFont.name ) || ( ! value && _x( 'Normal', 'font size name' ) ) || _x( 'Custom', 'font size name' ) } + { currentFontSizeName } </Button> ) } renderContent={ () => ( <NavigableMenu> - { map( fontSizes, ( { name, size, slug } ) => ( - <Button - key={ slug } - onClick={ () => onChange( slug === 'normal' ? undefined : size ) } - className={ 'is-font-' + slug } - role="menuitem" - > - { ( value === size || ( ! value && slug === 'normal' ) ) && <Dashicon icon="saved" /> } - <span className="components-font-size-picker__dropdown-text-size" style={ { fontSize: size } }> - { name } - </span> - </Button> - ) ) } + { map( fontSizes, ( { name, size, slug } ) => { + const isSelected = ( value === size || ( ! value && slug === 'normal' ) ); + + return ( + <Button + key={ slug } + onClick={ () => onChange( slug === 'normal' ? undefined : size ) } + className={ `is-font-${ slug }` } + role="menuitemradio" + aria-checked={ isSelected } + > + { isSelected && <Dashicon icon="saved" /> } + <span className="components-font-size-picker__dropdown-text-size" style={ { fontSize: size } }> + { name } + </span> + </Button> + ); + } ) } </NavigableMenu> ) } /> @@ -90,7 +98,6 @@ function FontSizePicker( { onClick={ () => onChange( undefined ) } isSmall isDefault - aria-label={ __( 'Reset font size' ) } > { __( 'Reset' ) } </Button> @@ -112,4 +119,4 @@ function FontSizePicker( { ); } -export default withInstanceId( FontSizePicker ); +export default FontSizePicker; diff --git a/packages/components/src/server-side-render/README.md b/packages/components/src/server-side-render/README.md index cccde285c645d..ee18a7f4294ed 100644 --- a/packages/components/src/server-side-render/README.md +++ b/packages/components/src/server-side-render/README.md @@ -2,7 +2,7 @@ ServerSideRender is a component used for server-side rendering a preview of dynamic blocks to display in the editor. Server-side rendering in a block's `edit` function should be limited to blocks that are heavily dependent on existing PHP rendering logic that is heavily intertwined with data, particularly when there are no endpoints available. -ServerSideRender may also be used when a legacy block is provided as a backwards compatibility measure, rather than needing to re-write the deprecated code that the block may depend on. +ServerSideRender may also be used when a legacy block is provided as a backward compatibility measure, rather than needing to re-write the deprecated code that the block may depend on. ServerSideRender should be regarded as a fallback or legacy mechanism, it is not appropriate for developing new features against. @@ -20,7 +20,7 @@ const MyServerSideRender = () => ( block="core/archives" attributes={ { showPostCounts: true, - displayAsDropdown: false, + displayAsDropdown: false, } } /> ); diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index 3fdf099954c98..e4b0c1ad32c68 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.1.5 (Unreleased) + +### Bug Fixes + - Fix saving WYSIWYG Meta Boxes + ## 3.1.4 (2018-11-30) ## 3.1.3 (2018-11-30) diff --git a/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js b/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js index 31bc0becf225f..970cf26355e6c 100644 --- a/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js +++ b/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js @@ -17,9 +17,16 @@ class MetaBoxVisibility extends Component { updateDOM() { const { id, isVisible } = this.props; + const element = document.getElementById( id ); - if ( element ) { - element.style.display = isVisible ? '' : 'none'; + if ( ! element ) { + return; + } + + if ( isVisible ) { + element.classList.remove( 'is-hidden' ); + } else { + element.classList.add( 'is-hidden' ); } } 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 f336804fa38de..b86d15952683b 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 @@ -75,6 +75,12 @@ right: 20px; z-index: z-index(".edit-post-meta-boxes-area .spinner"); } + + // Hide disabled meta boxes using CSS so that we don't interfere with plugins + // that modify `element.style.display` on the meta box. + .is-hidden { + display: none; + } } .edit-post-meta-boxes-area__clear { diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index bce8285f72b0b..59e870e7929da 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -68,6 +68,13 @@ export function initializeEditor( id, postType, postId, settings, initialEdits ) registerCoreBlocks(); + // Show a console log warning if the browser is not in Standards rendering mode. + const documentMode = document.compatMode === 'CSS1Compat' ? 'Standards' : 'Quirks'; + if ( documentMode !== 'Standards' ) { + // eslint-disable-next-line no-console + console.warn( "Your browser is using Quirks Mode. \nThis can cause rendering issues such as blocks overlaying meta boxes in the editor. Quirks Mode can be triggered by PHP errors or HTML code appearing before the opening <!DOCTYPE html>. Try checking the raw page source or your site's PHP error log and resolving errors there, removing any HTML before the doctype, or disabling plugins." ); + } + dispatch( 'core/nux' ).triggerGuide( [ 'core/editor.inserter', 'core/editor.settings', diff --git a/packages/edit-post/src/store/effects.js b/packages/edit-post/src/store/effects.js index 699b08f0636ed..a177c36c1b761 100644 --- a/packages/edit-post/src/store/effects.js +++ b/packages/edit-post/src/store/effects.js @@ -72,9 +72,14 @@ const effects = { } ); }, REQUEST_META_BOX_UPDATES( action, store ) { + // Saves the wp_editor fields + if ( window.tinyMCE ) { + window.tinyMCE.triggerSave(); + } + const state = store.getState(); - // Additional data needed for backwards compatibility. + // Additional data needed for backward compatibility. // If we do not provide this data, the post will be overridden with the default values. const post = select( 'core/editor' ).getCurrentPost( state ); const additionalData = [ diff --git a/packages/editor/src/components/default-block-appender/index.js b/packages/editor/src/components/default-block-appender/index.js index ecfe98d351a90..d4b95c4d961ba 100644 --- a/packages/editor/src/components/default-block-appender/index.js +++ b/packages/editor/src/components/default-block-appender/index.js @@ -7,7 +7,7 @@ import TextareaAutosize from 'react-autosize-textarea'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { compose } from '@wordpress/compose'; +import { compose, withState } from '@wordpress/compose'; import { getDefaultBlockName } from '@wordpress/blocks'; import { decodeEntities } from '@wordpress/html-entities'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -26,6 +26,8 @@ export function DefaultBlockAppender( { showPrompt, placeholder, rootClientId, + hovered, + setState, } ) { if ( isLocked || ! isVisible ) { return null; @@ -49,7 +51,12 @@ export function DefaultBlockAppender( { // The wp-block className is important for editor styles. return ( - <div data-root-client-id={ rootClientId || '' } className="wp-block editor-default-block-appender"> + <div + data-root-client-id={ rootClientId || '' } + className="wp-block editor-default-block-appender" + onMouseEnter={ () => setState( { hovered: true } ) } + onMouseLeave={ () => setState( { hovered: false } ) } + > <BlockDropZone rootClientId={ rootClientId } /> <TextareaAutosize role="button" @@ -59,12 +66,13 @@ export function DefaultBlockAppender( { onFocus={ onAppend } value={ showPrompt ? value : '' } /> - <InserterWithShortcuts rootClientId={ rootClientId } /> + { hovered && <InserterWithShortcuts rootClientId={ rootClientId } /> } <Inserter position="top right" /> </div> ); } export default compose( + withState( { hovered: false } ), withSelect( ( select, ownProps ) => { const { getBlockCount, getBlockName, isBlockValid, getEditorSettings, getTemplateLock } = select( 'core/editor' ); diff --git a/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap b/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap index df9daeebd54f2..dc26d4405d863 100644 --- a/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap +++ b/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap @@ -4,6 +4,8 @@ exports[`DefaultBlockAppender should append a default block when input focused 1 <div className="wp-block editor-default-block-appender" data-root-client-id="" + onMouseEnter={[Function]} + onMouseLeave={[Function]} > <WithDispatch(WithSelect(WithFilters(BlockDropZone))) /> <TextareaAutosize @@ -22,7 +24,6 @@ exports[`DefaultBlockAppender should append a default block when input focused 1 rows={1} value="Start writing or type / to choose a block" /> - <WithSelect(WithDispatch(InserterWithShortcuts)) /> <WithSelect(IfCondition(Inserter)) position="top right" /> @@ -33,6 +34,8 @@ exports[`DefaultBlockAppender should match snapshot 1`] = ` <div className="wp-block editor-default-block-appender" data-root-client-id="" + onMouseEnter={[Function]} + onMouseLeave={[Function]} > <WithDispatch(WithSelect(WithFilters(BlockDropZone))) /> <TextareaAutosize @@ -44,7 +47,6 @@ exports[`DefaultBlockAppender should match snapshot 1`] = ` rows={1} value="Start writing or type / to choose a block" /> - <WithSelect(WithDispatch(InserterWithShortcuts)) /> <WithSelect(IfCondition(Inserter)) position="top right" /> @@ -55,6 +57,8 @@ exports[`DefaultBlockAppender should optionally show without prompt 1`] = ` <div className="wp-block editor-default-block-appender" data-root-client-id="" + onMouseEnter={[Function]} + onMouseLeave={[Function]} > <WithDispatch(WithSelect(WithFilters(BlockDropZone))) /> <TextareaAutosize @@ -66,7 +70,6 @@ exports[`DefaultBlockAppender should optionally show without prompt 1`] = ` rows={1} value="" /> - <WithSelect(WithDispatch(InserterWithShortcuts)) /> <WithSelect(IfCondition(Inserter)) position="top right" /> diff --git a/packages/editor/src/components/post-preview-button/index.js b/packages/editor/src/components/post-preview-button/index.js index 5c31e9e271de8..b735ebcd5324e 100644 --- a/packages/editor/src/components/post-preview-button/index.js +++ b/packages/editor/src/components/post-preview-button/index.js @@ -80,6 +80,7 @@ function writeInterstitialMessage( targetDocument ) { `; targetDocument.write( markup ); + targetDocument.title = __( 'Generating preview…' ); targetDocument.close(); } diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index 872049c948e6a..0731916d6a1a7 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -134,12 +134,9 @@ export class RichText extends Component { } /** - * Handles the onSetup event for the TinyMCE component. + * Sets a reference to the TinyMCE editor instance. * - * Will setup event handlers for the TinyMCE instance. - * An `onSetup` function in the props will be called if it is present. - * - * @param {tinymce} editor The editor instance as passed by TinyMCE. + * @param {Editor} editor The editor instance as passed by TinyMCE. */ onSetup( editor ) { this.editor = editor; @@ -691,9 +688,12 @@ export class RichText extends Component { if ( shouldReapply ) { const record = this.formatToValue( value ); - // Maintain the previous selection: - record.start = this.state.start; - record.end = this.state.end; + // Maintain the previous selection if the instance is currently + // selected. + if ( isSelected ) { + record.start = this.state.start; + record.end = this.state.end; + } this.applyRecord( record ); } diff --git a/packages/editor/src/components/word-count/index.js b/packages/editor/src/components/word-count/index.js index 176435cdc76eb..719351ba32382 100644 --- a/packages/editor/src/components/word-count/index.js +++ b/packages/editor/src/components/word-count/index.js @@ -2,11 +2,19 @@ * WordPress dependencies */ import { withSelect } from '@wordpress/data'; +import { _x } from '@wordpress/i18n'; import { count as wordCount } from '@wordpress/wordcount'; function WordCount( { content } ) { + /* + * translators: If your word count is based on single characters (e.g. East Asian characters), + * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. + * Do not translate into your own language. + */ + const wordCountType = _x( 'words', 'Word count type. Do not translate!' ); + return ( - <span className="word-count">{ wordCount( content, 'words' ) }</span> + <span className="word-count">{ wordCount( content, wordCountType ) }</span> ); } diff --git a/packages/editor/src/hooks/anchor.js b/packages/editor/src/hooks/anchor.js index 66b1c4f01eef4..3af32e39ef2ac 100644 --- a/packages/editor/src/hooks/anchor.js +++ b/packages/editor/src/hooks/anchor.js @@ -98,7 +98,7 @@ export const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => */ export function addSaveProps( extraProps, blockType, attributes ) { if ( hasBlockSupport( blockType, 'anchor' ) ) { - extraProps.id = attributes.anchor; + extraProps.id = attributes.anchor === '' ? null : attributes.anchor; } return extraProps; diff --git a/packages/editor/src/hooks/test/anchor.js b/packages/editor/src/hooks/test/anchor.js index 74bfa2e20dfc2..21a5c65be83cb 100644 --- a/packages/editor/src/hooks/test/anchor.js +++ b/packages/editor/src/hooks/test/anchor.js @@ -62,5 +62,17 @@ describe( 'anchor', () => { expect( extraProps.id ).toBe( 'foo' ); } ); + + it( 'should remove an anchor attribute ID when feild is cleared', () => { + const attributes = { anchor: '' }; + const extraProps = getSaveContentExtraProps( {}, { + ...blockSettings, + supports: { + anchor: true, + }, + }, attributes ); + + expect( extraProps.id ).toBe( null ); + } ); } ); } ); diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 07a609d7278fa..8d5c6c93c35ee 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,5 +1,6 @@ { "name": "@wordpress/eslint-config", + "private": true, "version": "1.0.0-alpha.0", "description": "ESLint config for WordPress development.", "author": "The WordPress Contributors", diff --git a/packages/rich-text/src/to-dom.js b/packages/rich-text/src/to-dom.js index 1c34d8680dd2b..c811bbc618059 100644 --- a/packages/rich-text/src/to-dom.js +++ b/packages/rich-text/src/to-dom.js @@ -226,21 +226,17 @@ export function apply( { export function applyValue( future, current ) { let i = 0; + let futureChild; - while ( future.firstChild ) { + while ( ( futureChild = future.firstChild ) ) { const currentChild = current.childNodes[ i ]; - const futureNodeType = future.firstChild.nodeType; if ( ! currentChild ) { - current.appendChild( future.firstChild ); - } else if ( - futureNodeType !== currentChild.nodeType || - futureNodeType !== TEXT_NODE || - future.firstChild.nodeValue !== currentChild.nodeValue - ) { - current.replaceChild( future.firstChild, currentChild ); + current.appendChild( futureChild ); + } else if ( ! currentChild.isEqualNode( futureChild ) ) { + current.replaceChild( futureChild, currentChild ); } else { - future.removeChild( future.firstChild ); + future.removeChild( futureChild ); } i++; @@ -251,6 +247,25 @@ export function applyValue( future, current ) { } } +/** + * Returns true if two ranges are equal, or false otherwise. Ranges are + * considered equal if their start and end occur in the same container and + * offset. + * + * @param {Range} a First range object to test. + * @param {Range} b First range object to test. + * + * @return {boolean} Whether the two ranges are equal. + */ +function isRangeEqual( a, b ) { + return ( + a.startContainer === b.startContainer && + a.startOffset === b.startOffset && + a.endContainer === b.endContainer && + a.endOffset === b.endOffset + ); +} + export function applySelection( selection, current ) { const { node: startContainer, offset: startOffset } = getNodeByPath( current, selection.startPath ); const { node: endContainer, offset: endOffset } = getNodeByPath( current, selection.endPath ); @@ -283,6 +298,15 @@ export function applySelection( selection, current ) { range.setEnd( endContainer, endOffset ); } - windowSelection.removeAllRanges(); + if ( windowSelection.rangeCount > 0 ) { + // If the to be added range and the live range are the same, there's no + // need to remove the live range and add the equivalent range. + if ( isRangeEqual( range, windowSelection.getRangeAt( 0 ) ) ) { + return; + } + + windowSelection.removeAllRanges(); + } + windowSelection.addRange( range ); } diff --git a/packages/url/README.md b/packages/url/README.md index 4c092d670fc9f..fb78d8808bb2b 100644 --- a/packages/url/README.md +++ b/packages/url/README.md @@ -167,4 +167,20 @@ const newUrl = removeQueryArgs( 'https://wordpress.org?foo=bar&bar=baz&baz=fooba Removes one or more query string arguments from the given URL. +### safeDecodeURI + +```js +const badUri = safeDecodeURI( '%z' ); // does not throw an Error, simply returns '%z' +``` + +Safely decodes a URI with `decodeURI`. Returns the URI unmodified if `decodeURI` throws an Error. + +### filterURLForDisplay + +```js +const displayUrl = filterURLForDisplay( 'https://www.wordpress.org/gutenberg/' ); // wordpress.org/gutenberg +``` + +Returns a URL for display, without protocol, www subdomain, or trailing slash. + <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/viewport/src/store/selectors.js b/packages/viewport/src/store/selectors.js index ea8308d6ee156..d379beeb5052f 100644 --- a/packages/viewport/src/store/selectors.js +++ b/packages/viewport/src/store/selectors.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { takeRight } from 'lodash'; - /** * Returns true if the viewport matches the given query, or false otherwise. * @@ -20,9 +15,10 @@ import { takeRight } from 'lodash'; * @return {boolean} Whether viewport matches query. */ export function isViewportMatch( state, query ) { - // Pad to _at least_ two elements to take from the right, effectively - // defaulting the left-most value. - const key = takeRight( [ '>=', ...query.split( ' ' ) ], 2 ).join( ' ' ); + // Default to `>=` if no operator is present. + if ( query.indexOf( ' ' ) === -1 ) { + query = '>= ' + query; + } - return !! state[ key ]; + return !! state[ query ]; } diff --git a/phpunit/class-parsing-test.php b/phpunit/class-parsing-test.php deleted file mode 100644 index b854f8e306a2b..0000000000000 --- a/phpunit/class-parsing-test.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php -/** - * PEG.js parser (PHP code target) tests - * - * @package Gutenberg - */ - -class Parsing_Test extends WP_UnitTestCase { - protected static $fixtures_dir; - - function parsing_test_filenames() { - self::$fixtures_dir = dirname( dirname( __FILE__ ) ) . '/test/integration/full-content/fixtures'; - - require_once dirname( dirname( __FILE__ ) ) . '/lib/parser.php'; - - $fixture_filenames = array_merge( - glob( self::$fixtures_dir . '/*.json' ), - glob( self::$fixtures_dir . '/*.html' ) - ); - $fixture_filenames = array_values( - array_unique( - array_map( - array( $this, 'clean_fixture_filename' ), - $fixture_filenames - ) - ) - ); - - return array_map( - array( $this, 'pass_parser_fixture_filenames' ), - $fixture_filenames - ); - } - - function clean_fixture_filename( $filename ) { - $filename = basename( $filename ); - $filename = preg_replace( '/\..+$/', '', $filename ); - return $filename; - } - - function pass_parser_fixture_filenames( $filename ) { - return array( - "$filename.html", - "$filename.parsed.json", - ); - } - - function strip_r( $input ) { - return str_replace( "\r", '', $input ); - } - - /** - * @dataProvider parsing_test_filenames - */ - function test_spec_parser_output( $html_filename, $parsed_json_filename ) { - $html_path = self::$fixtures_dir . '/' . $html_filename; - $parsed_json_path = self::$fixtures_dir . '/' . $parsed_json_filename; - - foreach ( array( $html_path, $parsed_json_path ) as $filename ) { - if ( ! file_exists( $filename ) ) { - throw new Exception( "Missing fixture file: '$filename'" ); - } - } - - $html = self::strip_r( file_get_contents( $html_path ) ); - $expected_parsed = json_decode( self::strip_r( file_get_contents( $parsed_json_path ) ), true ); - - $parser = new Gutenberg_PEG_Parser; - $result = $parser->parse( _gutenberg_utf8_split( $html ) ); - - $this->assertEquals( - $expected_parsed, - $result, - "File '$parsed_json_filename' does not match expected value" - ); - } - - /** - * @dataProvider parsing_test_filenames - */ - function test_default_parser_output( $html_filename, $parsed_json_filename ) { - // include the parser if it was not yet loaded. - require_once dirname( __FILE__ ) . '/../packages/block-serialization-default-parser/parser.php'; - $html_path = self::$fixtures_dir . '/' . $html_filename; - $parsed_json_path = self::$fixtures_dir . '/' . $parsed_json_filename; - - foreach ( array( $html_path, $parsed_json_path ) as $filename ) { - if ( ! file_exists( $filename ) ) { - throw new Exception( "Missing fixture file: '$filename'" ); - } - } - - $html = self::strip_r( file_get_contents( $html_path ) ); - $expected_parsed = json_decode( self::strip_r( file_get_contents( $parsed_json_path ) ), true ); - - $parser = new WP_Block_Parser(); - $result = json_decode( json_encode( $parser->parse( $html ) ), true ); - - $this->assertEquals( - $expected_parsed, - $result, - "File '$parsed_json_filename' does not match expected value" - ); - } -} diff --git a/phpunit/class-rest-block-renderer-controller-test.php b/phpunit/class-rest-block-renderer-controller-test.php deleted file mode 100644 index 3691c4d162af4..0000000000000 --- a/phpunit/class-rest-block-renderer-controller-test.php +++ /dev/null @@ -1,454 +0,0 @@ -<?php -/** - * WP_REST_Block_Renderer_Controller tests. - * - * @package gutenberg - */ - -/** - * Tests for WP_REST_Block_Renderer_Controller. - * - * @covers WP_REST_Block_Renderer_Controller - */ -class REST_Block_Renderer_Controller_Test extends WP_Test_REST_Controller_Testcase { - - /** - * The REST API route for the block renderer. - * - * @var string - */ - protected static $rest_api_route = '/wp/v2/block-renderer/'; - - /** - * Test block's name. - * - * @var string - */ - protected static $block_name = 'core/test-block'; - - /** - * Test post context block's name. - * - * @var string - */ - protected static $context_block_name = 'core/context-test-block'; - - /** - * Test API user's ID. - * - * @var int - */ - protected static $user_id; - - /** - * Test post ID. - * - * @var int - */ - protected static $post_id; - - /** - * Author test user ID. - * - * @var int - */ - protected static $author_id; - - /** - * Create test data before the tests run. - * - * @param WP_UnitTest_Factory $factory Helper that lets us create fake data. - */ - public static function wpSetUpBeforeClass( $factory ) { - self::$user_id = $factory->user->create( - array( - 'role' => 'editor', - ) - ); - - self::$author_id = $factory->user->create( - array( - 'role' => 'author', - ) - ); - - self::$post_id = $factory->post->create( - array( - 'post_title' => 'Test Post', - ) - ); - } - - /** - * Delete test data after our tests run. - */ - public static function wpTearDownAfterClass() { - self::delete_user( self::$user_id ); - } - - /** - * Set up. - * - * @see gutenberg_register_rest_routes() - */ - public function setUp() { - $this->register_test_block(); - $this->register_post_context_test_block(); - parent::setUp(); - } - - /** - * Tear down. - */ - public function tearDown() { - WP_Block_Type_Registry::get_instance()->unregister( self::$block_name ); - WP_Block_Type_Registry::get_instance()->unregister( self::$context_block_name ); - parent::tearDown(); - } - - /** - * Register test block. - */ - public function register_test_block() { - register_block_type( - self::$block_name, - array( - 'attributes' => array( - 'some_string' => array( - 'type' => 'string', - 'default' => 'some_default', - ), - 'some_int' => array( - 'type' => 'integer', - ), - 'some_array' => array( - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - ), - ), - 'render_callback' => array( $this, 'render_test_block' ), - ) - ); - } - - /** - * Register test block with post_id as attribute for post context test. - */ - public function register_post_context_test_block() { - register_block_type( - self::$context_block_name, - array( - 'attributes' => array(), - 'render_callback' => array( $this, 'render_post_context_test_block' ), - ) - ); - } - - /** - * Test render callback. - * - * @param array $attributes Props. - * @return string Rendered attributes, which is here just JSON. - */ - public function render_test_block( $attributes ) { - return wp_json_encode( $attributes ); - } - - /** - * Test render callback for testing post context. - * - * @return string - */ - public function render_post_context_test_block() { - return get_the_title(); - } - - /** - * Check that the route was registered properly. - * - * @covers WP_REST_Block_Renderer_Controller::register_routes() - */ - public function test_register_routes() { - $dynamic_block_names = get_dynamic_block_names(); - $this->assertContains( self::$block_name, $dynamic_block_names ); - - $routes = rest_get_server()->get_routes(); - foreach ( $dynamic_block_names as $dynamic_block_name ) { - $this->assertArrayHasKey( self::$rest_api_route . "(?P<name>$dynamic_block_name)", $routes ); - } - } - - /** - * Test getting item without permissions. - * - * @covers WP_REST_Block_Renderer_Controller::get_item() - */ - public function test_get_item_without_permissions() { - wp_set_current_user( 0 ); - - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); - $request->set_param( 'context', 'edit' ); - - $response = rest_get_server()->dispatch( $request ); - - $this->assertErrorResponse( 'gutenberg_block_cannot_read', $response, rest_authorization_required_code() ); - } - - /** - * Test getting item without 'edit' context. - */ - public function test_get_item_with_invalid_context() { - wp_set_current_user( self::$user_id ); - - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); - $response = rest_get_server()->dispatch( $request ); - - $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); - } - - /** - * Test getting item with invalid block name. - * - * @covers WP_REST_Block_Renderer_Controller::get_item() - */ - public function test_get_item_invalid_block_name() { - wp_set_current_user( self::$user_id ); - $request = new WP_REST_Request( 'GET', self::$rest_api_route . 'core/123' ); - - $request->set_param( 'context', 'edit' ); - $response = rest_get_server()->dispatch( $request ); - - $this->assertErrorResponse( 'rest_no_route', $response, 404 ); - } - - /** - * Check getting item with an invalid param provided. - * - * @covers WP_REST_Block_Renderer_Controller::get_item() - */ - public function test_get_item_invalid_attribute() { - wp_set_current_user( self::$user_id ); - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); - $request->set_param( 'context', 'edit' ); - $request->set_param( - 'attributes', - array( - 'some_string' => array( 'no!' ), - ) - ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 400, $response->get_status() ); - } - - /** - * Check getting item with an invalid param provided. - * - * @covers WP_REST_Block_Renderer_Controller::get_item() - */ - public function test_get_item_unrecognized_attribute() { - wp_set_current_user( self::$user_id ); - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); - $request->set_param( 'context', 'edit' ); - $request->set_param( - 'attributes', - array( - 'unrecognized' => 'yes', - ) - ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 400, $response->get_status() ); - } - - /** - * Check getting item with default attributes provided. - * - * @covers WP_REST_Block_Renderer_Controller::get_item() - */ - public function test_get_item_default_attributes() { - wp_set_current_user( self::$user_id ); - - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( self::$block_name ); - $defaults = array(); - foreach ( $block_type->attributes as $key => $attribute ) { - if ( isset( $attribute['default'] ) ) { - $defaults[ $key ] = $attribute['default']; - } - } - - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); - $request->set_param( 'context', 'edit' ); - $request->set_param( 'attributes', array() ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - $data = $response->get_data(); - - $this->assertEquals( $defaults, json_decode( $data['rendered'], true ) ); - $this->assertEquals( - json_decode( $block_type->render( $defaults ) ), - json_decode( $data['rendered'] ) - ); - } - - /** - * Check getting item with attributes provided. - * - * @covers WP_REST_Block_Renderer_Controller::get_item() - */ - public function test_get_item() { - wp_set_current_user( self::$user_id ); - - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( self::$block_name ); - $attributes = array( - 'some_int' => '123', - 'some_string' => 'foo', - 'some_array' => array( 1, '2', 3 ), - ); - - $expected_attributes = $attributes; - $expected_attributes['some_int'] = (int) $expected_attributes['some_int']; - $expected_attributes['some_array'] = array_map( 'intval', $expected_attributes['some_array'] ); - - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); - $request->set_param( 'context', 'edit' ); - $request->set_param( 'attributes', $attributes ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - $data = $response->get_data(); - - $this->assertEquals( $expected_attributes, json_decode( $data['rendered'], true ) ); - $this->assertEquals( - json_decode( $block_type->render( $attributes ), true ), - json_decode( $data['rendered'], true ) - ); - } - - - - /** - * Check success response for getting item with layout attribute provided. - */ - public function test_get_item_with_layout() { - wp_set_current_user( self::$user_id ); - - $attributes = array( - 'layout' => 'foo', - ); - - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name ); - $request->set_param( 'context', 'edit' ); - $request->set_param( 'attributes', $attributes ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - } - - /** - * Test getting item with post context. - */ - public function test_get_item_with_post_context() { - wp_set_current_user( self::$user_id ); - - $expected_title = 'Test Post'; - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name ); - $request->set_param( 'context', 'edit' ); - - // Test without post ID. - $response = rest_get_server()->dispatch( $request ); - - $this->assertEquals( 200, $response->get_status() ); - $data = $response->get_data(); - - $this->assertTrue( empty( $data['rendered'] ) ); - - // Now test with post ID. - $request->set_param( 'post_id', self::$post_id ); - $response = rest_get_server()->dispatch( $request ); - - $this->assertEquals( 200, $response->get_status() ); - $data = $response->get_data(); - - $this->assertEquals( $expected_title, $data['rendered'] ); - } - - /** - * Test getting item with invalid post ID. - */ - public function test_get_item_without_permissions_invalid_post() { - wp_set_current_user( self::$user_id ); - - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name ); - $request->set_param( 'context', 'edit' ); - - // Test with invalid post ID. - $request->set_param( 'post_id', PHP_INT_MAX ); - $response = rest_get_server()->dispatch( $request ); - - $this->assertErrorResponse( 'gutenberg_block_cannot_read', $response, 403 ); - } - - /** - * Test getting item without permissions to edit context post. - */ - public function test_get_item_without_permissions_cannot_edit_post() { - wp_set_current_user( self::$author_id ); - - $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name ); - $request->set_param( 'context', 'edit' ); - - // Test with private post ID. - $request->set_param( 'post_id', self::$post_id ); - $response = rest_get_server()->dispatch( $request ); - - $this->assertErrorResponse( 'gutenberg_block_cannot_read', $response, 403 ); - } - - /** - * Get item schema. - * - * @covers WP_REST_Block_Renderer_Controller::get_item_schema() - */ - public function test_get_item_schema() { - $request = new WP_REST_Request( 'OPTIONS', self::$rest_api_route . self::$block_name ); - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - - $this->assertEqualSets( array( 'GET' ), $data['endpoints'][0]['methods'] ); - $this->assertEqualSets( - array( 'name', 'context', 'attributes', 'post_id' ), - array_keys( $data['endpoints'][0]['args'] ) - ); - $this->assertEquals( 'object', $data['endpoints'][0]['args']['attributes']['type'] ); - - $this->assertArrayHasKey( 'schema', $data ); - $this->assertEquals( 'rendered-block', $data['schema']['title'] ); - $this->assertEquals( 'object', $data['schema']['type'] ); - $this->arrayHasKey( 'rendered', $data['schema']['properties'] ); - $this->arrayHasKey( 'string', $data['schema']['properties']['rendered']['type'] ); - $this->assertEquals( array( 'edit' ), $data['schema']['properties']['rendered']['context'] ); - } - - public function test_update_item() { - $this->markTestSkipped( 'Controller doesn\'t implement update_item().' ); - } - - public function test_create_item() { - $this->markTestSkipped( 'Controller doesn\'t implement create_item().' ); - } - - public function test_delete_item() { - $this->markTestSkipped( 'Controller doesn\'t implement delete_item().' ); - } - - public function test_get_items() { - $this->markTestSkipped( 'Controller doesn\'t implement get_items().' ); - } - - public function test_context_param() { - $this->markTestSkipped( 'Controller doesn\'t implement context_param().' ); - } - - public function test_prepare_item() { - $this->markTestSkipped( 'Controller doesn\'t implement prepare_item().' ); - } -} diff --git a/phpunit/class-rest-blocks-controller-test.php b/phpunit/class-rest-blocks-controller-test.php deleted file mode 100644 index 5a9e71af7e33e..0000000000000 --- a/phpunit/class-rest-blocks-controller-test.php +++ /dev/null @@ -1,204 +0,0 @@ -<?php -/** - * WP_REST_Blocks_Controller tests - * - * @package gutenberg - */ - -/** - * Tests for WP_REST_Blocks_Controller. - */ -class REST_Blocks_Controller_Test extends WP_UnitTestCase { - - /** - * Our fake block's post ID. - * - * @var int - */ - protected static $post_id; - - /** - * Our fake user IDs, keyed by their role. - * - * @var array - */ - protected static $user_ids; - - /** - * Create fake data before our tests run. - * - * @param WP_UnitTest_Factory $factory Helper that lets us create fake data. - */ - public static function wpSetUpBeforeClass( $factory ) { - self::$post_id = wp_insert_post( - array( - 'post_type' => 'wp_block', - 'post_status' => 'publish', - 'post_title' => 'My cool block', - 'post_content' => '<!-- wp:paragraph --><p>Hello!</p><!-- /wp:paragraph -->', - ) - ); - - self::$user_ids = array( - 'editor' => $factory->user->create( array( 'role' => 'editor' ) ), - 'author' => $factory->user->create( array( 'role' => 'author' ) ), - 'contributor' => $factory->user->create( array( 'role' => 'contributor' ) ), - ); - } - - /** - * Delete our fake data after our tests run. - */ - public static function wpTearDownAfterClass() { - wp_delete_post( self::$post_id ); - - foreach ( self::$user_ids as $user_id ) { - self::delete_user( $user_id ); - } - } - - /** - * Test cases for test_capabilities(). - */ - public function data_capabilities() { - return array( - array( 'create', 'editor', 201 ), - array( 'create', 'author', 201 ), - array( 'create', 'contributor', 403 ), - array( 'create', null, 401 ), - - array( 'read', 'editor', 200 ), - array( 'read', 'author', 200 ), - array( 'read', 'contributor', 200 ), - array( 'read', null, 401 ), - - array( 'update_delete_own', 'editor', 200 ), - array( 'update_delete_own', 'author', 200 ), - array( 'update_delete_own', 'contributor', 403 ), - - array( 'update_delete_others', 'editor', 200 ), - array( 'update_delete_others', 'author', 403 ), - array( 'update_delete_others', 'contributor', 403 ), - array( 'update_delete_others', null, 401 ), - ); - } - - /** - * Exhaustively check that each role either can or cannot create, edit, - * update, and delete reusable blocks. - * - * @dataProvider data_capabilities - */ - public function test_capabilities( $action, $role, $expected_status ) { - if ( $role ) { - $user_id = self::$user_ids[ $role ]; - wp_set_current_user( $user_id ); - } else { - wp_set_current_user( 0 ); - } - - switch ( $action ) { - case 'create': - $request = new WP_REST_Request( 'POST', '/wp/v2/blocks' ); - $request->set_body_params( - array( - 'title' => 'Test', - 'content' => '<!-- wp:paragraph --><p>Test</p><!-- /wp:paragraph -->', - ) - ); - - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( $expected_status, $response->get_status() ); - - break; - - case 'read': - $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id ); - - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( $expected_status, $response->get_status() ); - - break; - - case 'update_delete_own': - $post_id = wp_insert_post( - array( - 'post_type' => 'wp_block', - 'post_status' => 'publish', - 'post_title' => 'My cool block', - 'post_content' => '<!-- wp:paragraph --><p>Hello!</p><!-- /wp:paragraph -->', - 'post_author' => $user_id, - ) - ); - - $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . $post_id ); - $request->set_body_params( - array( - 'title' => 'Test', - 'content' => '<!-- wp:paragraph --><p>Test</p><!-- /wp:paragraph -->', - ) - ); - - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( $expected_status, $response->get_status() ); - - $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . $post_id ); - - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( $expected_status, $response->get_status() ); - - wp_delete_post( $post_id ); - - break; - - case 'update_delete_others': - $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . self::$post_id ); - $request->set_body_params( - array( - 'title' => 'Test', - 'content' => '<!-- wp:paragraph --><p>Test</p><!-- /wp:paragraph -->', - ) - ); - - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( $expected_status, $response->get_status() ); - - $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id ); - - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( $expected_status, $response->get_status() ); - - break; - - default: - $this->fail( "'$action' is not a valid action." ); - } - } - - /** - * Check that the raw title and content of a block can be accessed when there - * is no set schema, and that the rendered content of a block is not included - * in the response. - */ - public function test_content() { - wp_set_current_user( self::$user_ids['author'] ); - - $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id ); - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - - $this->assertEquals( - array( - 'raw' => 'My cool block', - ), - $data['title'] - ); - $this->assertEquals( - array( - 'raw' => '<!-- wp:paragraph --><p>Hello!</p><!-- /wp:paragraph -->', - 'protected' => false, - ), - $data['content'] - ); - } -} diff --git a/phpunit/class-rest-search-controller-test.php b/phpunit/class-rest-search-controller-test.php deleted file mode 100644 index 26649037e8ff2..0000000000000 --- a/phpunit/class-rest-search-controller-test.php +++ /dev/null @@ -1,521 +0,0 @@ -<?php -/** - * WP_REST_Search_Controller tests - * - * @package gutenberg - */ - -/** - * Tests for WP_REST_Search_Controller. - */ -class REST_Search_Controller_Test extends WP_Test_REST_Controller_Testcase { - - /** - * Posts with title 'my-footitle'. - * - * @var array - */ - private static $my_title_post_ids = array(); - - /** - * Pages with title 'my-footitle'. - * - * @var array - */ - private static $my_title_page_ids = array(); - - /** - * Posts with content 'my-foocontent'. - * - * @var array - */ - private static $my_content_post_ids = array(); - - /** - * Create fake data before our tests run. - * - * @param WP_UnitTest_Factory $factory Helper that lets us create fake data. - */ - public static function wpSetUpBeforeClass( $factory ) { - self::$my_title_post_ids = $factory->post->create_many( - 4, - array( - 'post_title' => 'my-footitle', - 'post_type' => 'post', - ) - ); - - self::$my_title_page_ids = $factory->post->create_many( - 4, - array( - 'post_title' => 'my-footitle', - 'post_type' => 'page', - ) - ); - - self::$my_content_post_ids = $factory->post->create_many( - 6, - array( - 'post_content' => 'my-foocontent', - ) - ); - } - - /** - * Delete our fake data after our tests run. - */ - public static function wpTearDownAfterClass() { - $post_ids = array_merge( - self::$my_title_post_ids, - self::$my_title_page_ids, - self::$my_content_post_ids - ); - - foreach ( $post_ids as $post_id ) { - wp_delete_post( $post_id, true ); - } - } - - /** - * Check that our routes get set up properly. - */ - public function test_register_routes() { - $routes = rest_get_server()->get_routes(); - - $this->assertArrayHasKey( '/wp/v2/search', $routes ); - $this->assertCount( 1, $routes['/wp/v2/search'] ); - } - - /** - * Check the context parameter. - */ - public function test_context_param() { - $response = $this->do_request_with_params( array(), 'OPTIONS' ); - $data = $response->get_data(); - - $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] ); - $this->assertEquals( array( 'view', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] ); - } - - /** - * Search through all content. - */ - public function test_get_items() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEqualSets( - array_merge( - self::$my_title_post_ids, - self::$my_title_page_ids, - self::$my_content_post_ids - ), - wp_list_pluck( $response->get_data(), 'id' ) - ); - } - - /** - * Search through all content with a low limit. - */ - public function test_get_items_with_limit() { - $response = $this->do_request_with_params( - array( - 'per_page' => 3, - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEquals( 3, count( $response->get_data() ) ); - } - - /** - * Search through posts of any post type. - */ - public function test_get_items_search_type_post() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'type' => 'post', - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEqualSets( - array_merge( - self::$my_title_post_ids, - self::$my_title_page_ids, - self::$my_content_post_ids - ), - wp_list_pluck( $response->get_data(), 'id' ) - ); - } - - /** - * Search through posts of post type 'post'. - */ - public function test_get_items_search_type_post_subtype_post() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'type' => 'post', - 'subtype' => 'post', - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEqualSets( - array_merge( - self::$my_title_post_ids, - self::$my_content_post_ids - ), - wp_list_pluck( $response->get_data(), 'id' ) - ); - } - - /** - * Search through posts of post type 'page'. - */ - public function test_get_items_search_type_post_subtype_page() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'type' => 'post', - 'subtype' => 'page', - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEqualSets( - self::$my_title_page_ids, - wp_list_pluck( $response->get_data(), 'id' ) - ); - } - - /** - * Search through an invalid type - */ - public function test_get_items_search_type_invalid() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'type' => 'invalid', - ) - ); - - $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); - } - - /** - * Search through posts of an invalid post type. - */ - public function test_get_items_search_type_post_subtype_invalid() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'type' => 'post', - 'subtype' => 'invalid', - ) - ); - - $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); - } - - /** - * Search through posts and pages. - */ - public function test_get_items_search_posts_and_pages() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'type' => 'post', - 'subtype' => 'post,page', - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEqualSets( - array_merge( - self::$my_title_post_ids, - self::$my_title_page_ids, - self::$my_content_post_ids - ), - wp_list_pluck( $response->get_data(), 'id' ) - ); - } - - /** - * Search through all that matches a 'footitle' search. - */ - public function test_get_items_search_for_footitle() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'search' => 'footitle', - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEqualSets( - array_merge( - self::$my_title_post_ids, - self::$my_title_page_ids - ), - wp_list_pluck( $response->get_data(), 'id' ) - ); - } - - /** - * Search through all that matches a 'foocontent' search. - */ - public function test_get_items_search_for_foocontent() { - $response = $this->do_request_with_params( - array( - 'per_page' => 100, - 'search' => 'foocontent', - ) - ); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEqualSets( - self::$my_content_post_ids, - wp_list_pluck( $response->get_data(), 'id' ) - ); - } - - /** - * Test retrieving a single item isn't possible. - */ - public function test_get_item() { - /** The search controller does not allow getting individual item content */ - $request = new WP_REST_Request( 'GET', '/wp/v2/search' . self::$my_title_post_ids[0] ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 404, $response->get_status() ); - } - - /** - * Test creating an item isn't possible. - */ - public function test_create_item() { - /** The search controller does not allow creating content */ - $request = new WP_REST_Request( 'POST', '/wp/v2/search' ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 404, $response->get_status() ); - } - - /** - * Test updating an item isn't possible. - */ - public function test_update_item() { - /** The search controller does not allow upading content */ - $request = new WP_REST_Request( 'POST', '/wp/v2/search' . self::$my_title_post_ids[0] ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 404, $response->get_status() ); - } - - /** - * Test deleting an item isn't possible. - */ - public function test_delete_item() { - /** The search controller does not allow deleting content */ - $request = new WP_REST_Request( 'DELETE', '/wp/v2/search' . self::$my_title_post_ids[0] ); - $response = rest_get_server()->dispatch( $request ); - $this->assertEquals( 404, $response->get_status() ); - } - - /** - * Test preparing the data contains the correct fields. - */ - public function test_prepare_item() { - $response = $this->do_request_with_params(); - $this->assertEquals( 200, $response->get_status() ); - - $data = $response->get_data(); - $this->assertEquals( - array( - 'id', - 'title', - 'url', - 'type', - 'subtype', - '_links', - ), - array_keys( $data[0] ) - ); - } - - /** - * Test preparing the data with limited fields contains the correct fields. - */ - public function test_prepare_item_limit_fields() { - if ( ! method_exists( 'WP_REST_Controller', 'get_fields_for_response' ) ) { - $this->markTestSkipped( 'Limiting fields requires the WP_REST_Controller::get_fields_for_response() method.' ); - } - - $response = $this->do_request_with_params( - array( - '_fields' => 'id,title', - ) - ); - $this->assertEquals( 200, $response->get_status() ); - - $data = $response->get_data(); - $this->assertEquals( - array( - 'id', - 'title', - '_links', - ), - array_keys( $data[0] ) - ); - } - - /** - * Tests the item schema is correct. - */ - public function test_get_item_schema() { - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/search' ); - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - $properties = $data['schema']['properties']; - - $this->assertArrayHasKey( 'id', $properties ); - $this->assertArrayHasKey( 'title', $properties ); - $this->assertArrayHasKey( 'url', $properties ); - $this->assertArrayHasKey( 'type', $properties ); - $this->assertArrayHasKey( 'subtype', $properties ); - } - - /** - * Tests that non-public post types are not allowed. - */ - public function test_non_public_post_type() { - $response = $this->do_request_with_params( - array( - 'type' => 'post', - 'subtype' => 'post,nav_menu_item', - ) - ); - $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); - } - - /** - * Test getting items directly with a custom search handler. - */ - public function test_custom_search_handler_get_items() { - $controller = new WP_REST_Search_Controller( array( new WP_REST_Dummy_Search_Handler( 10 ) ) ); - - $request = $this->get_request( - array( - 'page' => 1, - 'per_page' => 10, - 'type' => 'dummy', - 'subtype' => array( WP_REST_Search_Controller::TYPE_ANY ), - ) - ); - $response = $controller->get_items( $request ); - $this->assertEqualSets( range( 1, 10 ), wp_list_pluck( $response->get_data(), 'id' ) ); - - $request = $this->get_request( - array( - 'page' => 1, - 'per_page' => 10, - 'type' => 'dummy', - 'subtype' => array( 'dummy_first_type' ), - ) - ); - $response = $controller->get_items( $request ); - $this->assertEqualSets( range( 1, 5 ), wp_list_pluck( $response->get_data(), 'id' ) ); - } - - /** - * Test preparing an item directly with a custom search handler. - */ - public function test_custom_search_handler_prepare_item() { - $controller = new WP_REST_Search_Controller( array( new WP_REST_Dummy_Search_Handler( 10 ) ) ); - - $request = $this->get_request( - array( - 'type' => 'dummy', - 'subtype' => array( WP_REST_Search_Controller::TYPE_ANY ), - ) - ); - $response = $controller->prepare_item_for_response( 1, $request ); - $data = $response->get_data(); - $this->assertEquals( - array( - 'id', - 'title', - 'url', - 'type', - 'subtype', - ), - array_keys( $data ) - ); - } - - /** - * Test preparing an item directly with a custom search handler with limited fields. - */ - public function test_custom_search_handler_prepare_item_limit_fields() { - if ( ! method_exists( 'WP_REST_Controller', 'get_fields_for_response' ) ) { - $this->markTestSkipped( 'Limiting fields requires the WP_REST_Controller::get_fields_for_response() method.' ); - } - - $controller = new WP_REST_Search_Controller( array( new WP_REST_Dummy_Search_Handler( 10 ) ) ); - - $request = $this->get_request( - array( - 'type' => 'dummy', - 'subtype' => array( WP_REST_Search_Controller::TYPE_ANY ), - '_fields' => 'id,title', - ) - ); - $response = $controller->prepare_item_for_response( 1, $request ); - $data = $response->get_data(); - $this->assertEquals( - array( - 'id', - 'title', - ), - array_keys( $data ) - ); - } - - /** - * Test getting the collection params directly with a custom search handler. - */ - public function test_custom_search_handler_get_collection_params() { - $controller = new WP_REST_Search_Controller( array( new WP_REST_Dummy_Search_Handler( 10 ) ) ); - - $params = $controller->get_collection_params(); - $this->assertEquals( 'dummy', $params[ WP_REST_Search_Controller::PROP_TYPE ]['default'] ); - $this->assertEqualSets( array( 'dummy' ), $params[ WP_REST_Search_Controller::PROP_TYPE ]['enum'] ); - $this->assertEqualSets( array( 'dummy_first_type', 'dummy_second_type', WP_REST_Search_Controller::TYPE_ANY ), $params[ WP_REST_Search_Controller::PROP_SUBTYPE ]['items']['enum'] ); - } - - /** - * Perform a REST request to our search endpoint with given parameters. - */ - private function do_request_with_params( $params = array(), $method = 'GET' ) { - $request = $this->get_request( $params, $method ); - - return rest_get_server()->dispatch( $request ); - } - - /** - * Get a REST request object for given parameters. - */ - private function get_request( $params = array(), $method = 'GET' ) { - $request = new WP_REST_Request( $method, '/wp/v2/search' ); - - foreach ( $params as $param => $value ) { - $request->set_param( $param, $value ); - } - - return $request; - } -} diff --git a/test/e2e/specs/__snapshots__/rich-text.test.js.snap b/test/e2e/specs/__snapshots__/rich-text.test.js.snap index e911f407edd68..275080b436f2d 100644 --- a/test/e2e/specs/__snapshots__/rich-text.test.js.snap +++ b/test/e2e/specs/__snapshots__/rich-text.test.js.snap @@ -24,6 +24,12 @@ exports[`RichText should handle change in tag name gracefully 1`] = ` <!-- /wp:heading -->" `; +exports[`RichText should only mutate text data on input 1`] = ` +"<!-- wp:paragraph --> +<p>1<strong>2</strong>34</p> +<!-- /wp:paragraph -->" +`; + exports[`RichText should transform backtick to code 1`] = ` "<!-- wp:paragraph --> <p>A <code>backtick</code></p> diff --git a/test/e2e/specs/__snapshots__/wp-editor-meta-box.test.js.snap b/test/e2e/specs/__snapshots__/wp-editor-meta-box.test.js.snap new file mode 100644 index 0000000000000..485862873b648 --- /dev/null +++ b/test/e2e/specs/__snapshots__/wp-editor-meta-box.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`WP Editor Meta Boxes Should save the changes 1`] = `"<p>Typing in a metabox</p>"`; diff --git a/test/e2e/specs/blocks/__snapshots__/classic.test.js.snap b/test/e2e/specs/blocks/__snapshots__/classic.test.js.snap new file mode 100644 index 0000000000000..a4461344bb438 --- /dev/null +++ b/test/e2e/specs/blocks/__snapshots__/classic.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Classic should be inserted 1`] = `"test"`; diff --git a/test/e2e/specs/blocks/classic.test.js b/test/e2e/specs/blocks/classic.test.js new file mode 100644 index 0000000000000..f0b733b4a9947 --- /dev/null +++ b/test/e2e/specs/blocks/classic.test.js @@ -0,0 +1,73 @@ +/** + * External dependencies + */ +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import uuid from 'uuid/v4'; + +/** + * Internal dependencies + */ +import { + getEditedPostContent, + newPost, + insertBlock, + pressWithModifier, +} from '../../support/utils'; + +describe( 'Classic', () => { + beforeEach( async () => { + await newPost(); + } ); + + it( 'should be inserted', async () => { + await insertBlock( 'Classic' ); + // Wait for TinyMCE to initialise. + await page.waitForSelector( '.mce-content-body' ); + // Ensure there is focus. + await page.focus( '.mce-content-body' ); + await page.keyboard.type( 'test' ); + // Move focus away. + await pressWithModifier( 'shift', 'Tab' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); + + it( 'should insert media', async () => { + await insertBlock( 'Classic' ); + // Wait for TinyMCE to initialise. + await page.waitForSelector( '.mce-content-body' ); + // Ensure there is focus. + await page.focus( '.mce-content-body' ); + await page.keyboard.type( 'test' ); + + // Click the image button. + await page.waitForSelector( 'div[aria-label="Add Media"]' ); + await page.click( 'div[aria-label="Add Media"]' ); + + // 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 filename = uuid(); + const tmpFileName = path.join( os.tmpdir(), filename + '.png' ); + fs.copyFileSync( testImagePath, tmpFileName ); + await inputElement.uploadFile( tmpFileName ); + + // Wait for upload. + await page.waitForSelector( `.media-modal li[aria-label="${ filename }"]` ); + + // Insert the uploaded image. + await page.click( '.media-modal button.media-button-insert' ); + + // Wait for image to be inserted. + await page.waitForSelector( '.mce-content-body img' ); + + // Move focus away. + await pressWithModifier( 'shift', 'Tab' ); + + const regExp = new RegExp( `test<img class="alignnone size-full wp-image-\\d+" src="[^"]+\\/${ filename }\\.png" alt="" width="10" height="10" \\/>` ); + expect( await getEditedPostContent() ).toMatch( regExp ); + } ); +} ); diff --git a/test/e2e/specs/classic-editor.test.js b/test/e2e/specs/classic-editor.test.js deleted file mode 100644 index 0d51c93e5b342..0000000000000 --- a/test/e2e/specs/classic-editor.test.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Internal dependencies - */ -import { visitAdmin } from '../support/utils'; - -describe( 'classic editor', () => { - beforeAll( async () => { - await visitAdmin( 'post-new.php', 'classic-editor' ); - } ); - - it( 'Should work properly', async () => { - // Click visual editor - await expect( page ).toClick( '#content-tmce' ); - await expect( page ).toClick( '#content_ifr' ); - - // type some random text - await page.keyboard.type( 'Typing in classic editor' ); - - // Switch to HTML mode - await expect( page ).toClick( '#content-html' ); - - const textEditorContent = await page.$eval( '.wp-editor-area', ( element ) => element.value ); - expect( textEditorContent ).toEqual( 'Typing in classic editor' ); - } ); -} ); diff --git a/test/e2e/specs/font-size-picker.test.js b/test/e2e/specs/font-size-picker.test.js index 4a55dd3e0dbc8..42bfe7e17f5a4 100644 --- a/test/e2e/specs/font-size-picker.test.js +++ b/test/e2e/specs/font-size-picker.test.js @@ -5,6 +5,7 @@ import { clickBlockAppender, getEditedPostContent, newPost, + pressTimes, } from '../support/utils'; describe( 'Font Size Picker', () => { @@ -32,7 +33,8 @@ describe( 'Font Size Picker', () => { await page.keyboard.type( 'Paragraph to be made "small"' ); await page.click( '.blocks-font-size .components-range-control__number' ); - await page.keyboard.type( '13' ); + // This should be the "small" font-size of the current theme. + await page.keyboard.type( '19.5' ); // Ensure content matches snapshot. const content = await getEditedPostContent(); @@ -58,7 +60,8 @@ describe( 'Font Size Picker', () => { await page.keyboard.type( 'Paragraph with font size reset using button' ); await page.click( '.blocks-font-size .components-range-control__number' ); - await page.keyboard.type( '13' ); + // This should be the default font-size of the current theme. + await page.keyboard.type( '22' ); // Blur the range control await page.click( '.components-base-control__label' ); @@ -80,9 +83,10 @@ describe( 'Font Size Picker', () => { const changeSizeButton = await page.waitForSelector( '.components-button.is-font-large' ); await changeSizeButton.click(); + // Clear the custom font size input. await page.click( '.blocks-font-size .components-range-control__number' ); - await page.keyboard.press( 'Backspace' ); - await page.keyboard.press( 'Backspace' ); + await pressTimes( 'ArrowRight', 4 ); + await pressTimes( 'Backspace', 4 ); // Ensure content matches snapshot. const content = await getEditedPostContent(); diff --git a/test/e2e/specs/new-post.test.js b/test/e2e/specs/new-post.test.js index da171371330db..44e08f1902f2d 100644 --- a/test/e2e/specs/new-post.test.js +++ b/test/e2e/specs/new-post.test.js @@ -2,12 +2,19 @@ * Internal dependencies */ import { newPost } from '../support/utils'; +import { activatePlugin, deactivatePlugin } from '../support/plugins'; describe( 'new editor state', () => { beforeAll( async () => { + await activatePlugin( 'gutenberg-test-plugin-post-formats-support' ); await newPost(); } ); + afterAll( async () => { + await newPost(); + await deactivatePlugin( 'gutenberg-test-plugin-post-formats-support' ); + } ); + it( 'should show the New Post page in Gutenberg', async () => { expect( page.url() ).toEqual( expect.stringContaining( 'post-new.php' ) ); // Should display the blank title. diff --git a/test/e2e/specs/rich-text.test.js b/test/e2e/specs/rich-text.test.js index dca248c828931..2f1012766444f 100644 --- a/test/e2e/specs/rich-text.test.js +++ b/test/e2e/specs/rich-text.test.js @@ -67,4 +67,75 @@ describe( 'RichText', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'should only mutate text data on input', async () => { + await clickBlockAppender(); + await page.keyboard.type( '1' ); + await pressWithModifier( 'primary', 'b' ); + await page.keyboard.type( '2' ); + await pressWithModifier( 'primary', 'b' ); + await page.keyboard.type( '3' ); + + await page.evaluate( () => { + let called; + const { body } = document; + const config = { + attributes: true, + childList: true, + characterData: true, + subtree: true, + }; + + const mutationObserver = new MutationObserver( ( records ) => { + if ( called || records.length > 1 ) { + throw new Error( 'Typing should only mutate once.' ); + } + + records.forEach( ( record ) => { + if ( record.type !== 'characterData' ) { + throw new Error( + `Typing mutated more than character data: ${ record.type }` + ); + } + } ); + + called = true; + } ); + + mutationObserver.observe( body, config ); + + window.unsubscribes = [ () => mutationObserver.disconnect() ]; + + document.addEventListener( 'selectionchange', () => { + function throwMultipleSelectionChange() { + throw new Error( 'Typing should only emit one selection change event.' ); + } + + document.addEventListener( + 'selectionchange', + throwMultipleSelectionChange, + { once: true } + ); + + window.unsubscribes.push( () => { + document.removeEventListener( 'selectionchange', throwMultipleSelectionChange ); + } ); + }, { once: true } ); + } ); + + await page.keyboard.type( '4' ); + + await page.evaluate( () => { + // The selection change event should be called once. If there's only + // one item in `window.unsubscribes`, it means that only one + // function is present to disconnect the `mutationObserver`. + if ( window.unsubscribes.length === 1 ) { + throw new Error( 'The selection change event listener was never called.' ); + } + + window.unsubscribes.forEach( ( unsubscribe ) => unsubscribe() ); + } ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } ); diff --git a/test/e2e/specs/templates.test.js b/test/e2e/specs/templates.test.js index 9b5d22080e0d4..4d8d16e9ce951 100644 --- a/test/e2e/specs/templates.test.js +++ b/test/e2e/specs/templates.test.js @@ -72,8 +72,14 @@ describe( 'templates', () => { await switchToTestUser(); } - beforeAll( async () => await setPostFormat( 'image' ) ); - afterAll( async () => await setPostFormat( STANDARD_FORMAT_VALUE ) ); + beforeAll( async () => { + await activatePlugin( 'gutenberg-test-plugin-post-formats-support' ); + await setPostFormat( 'image' ); + } ); + afterAll( async () => { + await setPostFormat( STANDARD_FORMAT_VALUE ); + await deactivatePlugin( 'gutenberg-test-plugin-post-formats-support' ); + } ); it( 'should populate new post with default block for format', async () => { await newPost(); diff --git a/test/e2e/specs/wp-editor-meta-box.test.js b/test/e2e/specs/wp-editor-meta-box.test.js new file mode 100644 index 0000000000000..496057bb55c1c --- /dev/null +++ b/test/e2e/specs/wp-editor-meta-box.test.js @@ -0,0 +1,38 @@ +/** + * Internal dependencies + */ +import { newPost, publishPost } from '../support/utils'; +import { activatePlugin, deactivatePlugin } from '../support/plugins'; + +describe( 'WP Editor Meta Boxes', () => { + beforeAll( async () => { + await activatePlugin( 'gutenberg-test-plugin-wp-editor-meta-box' ); + await newPost(); + } ); + + afterAll( async () => { + await deactivatePlugin( 'gutenberg-test-plugin-wp-editor-meta-box' ); + } ); + + it( 'Should save the changes', async () => { + // Add title to enable valid non-empty post save. + await page.type( '.editor-post-title__input', 'Hello Meta' ); + + // Type something + await page.click( '#test_tinymce_id-html' ); + await page.type( '#test_tinymce_id', 'Typing in a metabox' ); + await page.click( '#test_tinymce_id-tmce' ); + + await publishPost(); + + await page.reload(); + + await page.click( '#test_tinymce_id-html' ); + const content = await page.$eval( + '#test_tinymce_id', + ( textarea ) => textarea.value + ); + + expect( content ).toMatchSnapshot(); + } ); +} ); diff --git a/test/e2e/specs/writing-flow.test.js b/test/e2e/specs/writing-flow.test.js index bcad1f3d55bb0..7c4197755bab8 100644 --- a/test/e2e/specs/writing-flow.test.js +++ b/test/e2e/specs/writing-flow.test.js @@ -44,6 +44,8 @@ describe( 'adding blocks', () => { // Arrow up in inner blocks should navigate through (1) column wrapper, // (2) text fields. + // We need to arrow up key presses in the paragraph block because it shows up in two lines. + await page.keyboard.press( 'ArrowUp' ); await page.keyboard.press( 'ArrowUp' ); await page.keyboard.press( 'ArrowUp' ); activeElementText = await page.evaluate( () => document.activeElement.textContent ); @@ -53,6 +55,7 @@ describe( 'adding blocks', () => { // columns wrappers before escaping out. let activeElementBlockType; await page.keyboard.press( 'ArrowUp' ); + await page.keyboard.press( 'ArrowUp' ); activeElementBlockType = await page.evaluate( () => ( document.activeElement.getAttribute( 'data-type' ) ) ); diff --git a/test/e2e/support/plugins.js b/test/e2e/support/plugins.js index da9edd3656860..f8d64a7a1ec17 100644 --- a/test/e2e/support/plugins.js +++ b/test/e2e/support/plugins.js @@ -11,7 +11,7 @@ import { visitAdmin, switchToAdminUser, switchToTestUser } from './utils'; */ export async function installPlugin( slug, searchTerm ) { await switchToAdminUser(); - await visitAdmin( 'plugin-install.php?s=' + encodeURIComponent( searchTerm || slug ) + '&tab=search&type=term' ); + await visitAdmin( 'plugin-install.php', 's=' + encodeURIComponent( searchTerm || slug ) + '&tab=search&type=term' ); await page.click( '.install-now[data-slug="' + slug + '"]' ); await page.waitForSelector( '.activate-now[data-slug="' + slug + '"]' ); await switchToTestUser(); diff --git a/test/e2e/test-plugins/post-formats.php b/test/e2e/test-plugins/post-formats.php new file mode 100644 index 0000000000000..b0fdec68947fc --- /dev/null +++ b/test/e2e/test-plugins/post-formats.php @@ -0,0 +1,15 @@ +<?php +/** + * Plugin Name: Gutenberg Test Plugin, Post Formats Support + * Plugin URI: https://github.com/WordPress/gutenberg + * Author: Gutenberg Team + * + * @package gutenberg-test-plugin-post-formats + */ + +add_theme_support( 'post-formats', array( 'image', 'gallery' ) ); +add_action( 'init', 'gutenberg_test_plugin_post_formats_add_post_support', 11 ); + +function gutenberg_test_plugin_post_formats_add_post_support() { + add_post_type_support( 'page', 'post-formats' ); +} diff --git a/test/e2e/test-plugins/wp-editor-metabox.php b/test/e2e/test-plugins/wp-editor-metabox.php new file mode 100644 index 0000000000000..088fd2ee6e87a --- /dev/null +++ b/test/e2e/test-plugins/wp-editor-metabox.php @@ -0,0 +1,27 @@ +<?php +/** + * Plugin Name: Gutenberg Test Plugin, WP Editor Meta Box + * Plugin URI: https://github.com/WordPress/gutenberg + * Author: Gutenberg Team + * + * @package gutenberg-test-wp-editor-metabox + */ + +add_action( 'add_meta_boxes', function(){ + add_meta_box( 'test_tinymce', 'Test TinyMCE', function( $post ){ + $field_value = get_post_meta( $post->ID, 'test_tinymce', true ); + wp_editor( $field_value, 'test_tinymce_id', array( + 'wpautop' => true, + 'media_buttons' => false, + 'textarea_name' => 'test_tinymce', + 'textarea_rows' => 10, + 'teeny' => true + ) ); + }, null, 'advanced', 'high' ); +}); +add_action( 'save_post', function( $post_id ){ + if ( ! isset( $_POST['test_tinymce'] ) ) { + return; + } + update_post_meta( $post_id, 'test_tinymce', $_POST['test_tinymce'] ); +}); From 81843bd8aa68e37ab9652c1e1ade215cfaf8d3c6 Mon Sep 17 00:00:00 2001 From: Pinar Olguc <pinarolguc@gmail.com> Date: Tue, 11 Dec 2018 16:34:51 +0300 Subject: [PATCH 08/15] Update native more block styling (#12767) * Update styling of more block * Fix CI test * Update spaces * Convert indentation to tabs * Fix lint issues * Remove key attribute * Set fixed width to the text input --- .../block-library/src/more/edit.native.js | 44 ++++++++++++------- .../block-library/src/more/editor.native.scss | 25 ++++++----- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index a6845e2ade8a5..da731dd953f13 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -47,29 +47,39 @@ export default class MoreEdit extends Component { this.props.setAttributes( { customText: value } ); } - render() { + renderLine() { + return <View style={ styles[ 'block-library-more__line' ] } /> + } + + renderText() { const { attributes, onFocus, onBlur } = this.props; const { customText } = attributes; const defaultText = __( 'Read more' ); const value = customText !== undefined ? customText : defaultText; return ( - <View style={ styles[ 'block-library-more__container' ] }> - <View style={ styles[ 'block-library-more__sub-container' ] }> - <Text style={ styles[ 'block-library-more__left-marker' ] }>&lt;!--</Text> - <PlainText - style={ styles[ 'block-library-more__plain-text' ] } - value={ value } - multiline={ true } - underlineColorAndroid="transparent" - onChange={ this.onChangeInput } - placeholder={ defaultText } - isSelected={ this.props.isSelected } - onFocus={ onFocus } - onBlur={ onBlur } - /> - <Text style={ styles[ 'block-library-more__right-marker' ] }>--&gt;</Text> - </View> + <View> + <PlainText + style={ styles[ 'block-library-more__text' ] } + value={ value } + multiline={ true } + underlineColorAndroid="transparent" + onChange={ this.onChangeInput } + placeholder={ defaultText } + isSelected={ this.props.isSelected } + onFocus={ onFocus } + onBlur={ onBlur } + /> + </View> + ) + } + + render() { + return ( + <View style={ styles[ 'block-library-more__container' ]}> + { this.renderLine() } + { this.renderText() } + { this.renderLine() } </View> ); } diff --git a/packages/block-library/src/more/editor.native.scss b/packages/block-library/src/more/editor.native.scss index 46d36fe08e321..beb5ef423776f 100644 --- a/packages/block-library/src/more/editor.native.scss +++ b/packages/block-library/src/more/editor.native.scss @@ -2,21 +2,22 @@ .block-library-more__container { align-items: center; - padding-left: 4; - padding-right: 4; - padding-top: 4; - padding-bottom: 4; -} - -.block-library-more__sub-container { - align-items: center; + padding: 4px; flex-direction: row; } -.block-library-more__left-marker { - padding-right: 4; +.block-library-more__line { + background-color: #555d66; + height: 2; + flex: 1; } -.block-library-more__right-marker { - padding-left: 4; +.block-library-more__text { + text-decoration-style: solid; + flex: 0; + width: 200; + text-align: center; + margin-left: 15; + margin-right: 15; + margin-bottom: 5; } From e72c26b35f4af54a412798cc9b0d4a24559abdba Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Wed, 12 Dec 2018 16:52:59 +0100 Subject: [PATCH 09/15] Make sure to properly compare empty text and undefined text variable before resetting the eventCount field. (#12815) --- packages/editor/src/components/rich-text/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js index d909063d832a7..9ca46d027e2e4 100644 --- a/packages/editor/src/components/rich-text/index.native.js +++ b/packages/editor/src/components/rich-text/index.native.js @@ -274,8 +274,8 @@ export class RichText extends Component { // If the component is changed React side (undo/redo/merging/splitting/custom text actions) // we need to make sure the native is updated as well - if ( nextProps.value && - this.lastContent && + if ( ( typeof nextProps.value !== 'undefined' ) && + ( typeof this.lastContent !== 'undefined' ) && nextProps.value !== this.lastContent ) { this.lastEventCount = undefined; // force a refresh on the native side } From 57c9a31602067b8328713b30607212613b98de60 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Thu, 13 Dec 2018 09:39:07 +0100 Subject: [PATCH 10/15] Use the default "Read More" text on init only, giving the ability to user to empty the field and re-start with empty text (#12821) --- packages/block-library/src/more/edit.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index da731dd953f13..2a17104d3077b 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -54,7 +54,7 @@ export default class MoreEdit extends Component { renderText() { const { attributes, onFocus, onBlur } = this.props; const { customText } = attributes; - const defaultText = __( 'Read more' ); + const { defaultText } = this.state; const value = customText !== undefined ? customText : defaultText; return ( From e81aecf897dcd6789e686e697cfccc5385f8260d Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler <dekervit@gmail.com> Date: Thu, 13 Dec 2018 14:48:11 +0100 Subject: [PATCH 11/15] Merge 'origin/master' into 'origin/mobile' (#12836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * RichText: fix onSetup doc (#12607) * Update broken links (#12660) * Update broken links There was 2 broken links. I changed same way with Data Module Reference page. * Update docs/designers-developers/developers/block-api/README.md Co-Authored-By: cagdasdag <cagdasdag81@gmail.com> * Update docs/designers-developers/developers/block-api/README.md Co-Authored-By: cagdasdag <cagdasdag81@gmail.com> * Docs: Update Glossary (#12479) * Update glossar with missing terms * Convert glossary to use dl/dt/dd dtags. Fixes #9976 * Fix TinyMCE link * remove spacing around tags * Add template definition, with link * Updates per review * Update docs/designers-developers/glossary.md Co-Authored-By: mkaz <marcus@mkaz.com> * Add documentation for `safeDecodeURI()` and `filterURLForDisplay()` (#12570) * Add documentation for `safeDecodeURI()` and `filterURLForDisplay()` * Whitespace * Consistant Capit… i mean capitalization * Oxford comma Co-Authored-By: georgeh <george@hotelling.net> * Update theme-support.md (#12661) * Fix: Undoing Image Selection in Image Block results in Broken Image (#12567) * Optimize isViewportMatch (#12542) * Cache createBlock call in isUnmodifiedDefaultBlock (#12521) * Cache createBlock call in isUnmodifiedDefaultBlock * Invalidate cache when default block name changes * Merge ifs * Font Size Picker: Use a menuitemradio role and better labels. (#12372) * Use a menuitemradio role and better labels. * Restore Button and remove MenuItem import. * Use template literals. * Set document title for preview interstitial (#12466) * Fix e2e tests after the WordPress 5.0 upgrade (#12715) * Fix e2e tests after the WordPress 5.0 upgrade * Remove the php unit tests testing the WP5.0 core instead of the plugin * Meta Boxes: Don't hide disabled meta boxes by modifying DOM (#12628) Hiding disabled meta boxes by setting `element.style.display = 'none'` interferes with plugins like ACF which rely on being able to show and hide meta boxes using `$.hide()` and `$.show()`. Hiding the meta box using a new `.edit-post-meta-boxes-area .is-hidden` class ensures that we don't interfere with third party code. * Add a word-wrap style to the facebook embed preview screen (#11890) * Add a word-break style to the facebook embed preview screen to prevent the long embed url from breaking the block boundary * Fix typo and missing space in scss comment * Adding @aldavigdis to the contributors list (#12686) * Make media & text block placeholder translatable (#12706) * Fix: Problems on Media & Text block resizing; Load wp-block-library styles before wp-edit-blocks. (#12619) * Get wordcount type from translation (#12586) * get wordcount type from translation * Add description to explain the options for wordcount type * Added mylsef into contributors.md. :) * Only render InserterWithShortcuts on hover (#12510) * Fix issue where default appender has icons overlaying the text (#12536) * Fix issue where default appender has icons overlaying the text This fixes #11425. It adds padding to the right of the default block appender to fit 3 icons. * chore: Tweak spelling * Classic Block: set correct focus back after blur (#12415) * Classic Block: set correct focus back after blur * Add e2e test * reset bookmark on mousedown and touchstart * e2e: Look for aria-label="Add Media" rather than "Insert Media" * RichText: only replace range and nodes if different (#12547) * RichText: only set range if different * Check rangeCount * Also compare nodes * Add e2e test * Simplify * RichText: Document isRangeEqual * Testing: RichText: Assure subscriber removal * Unsubscribe in page.evaluate * Mark temporary eslint-config package as private (#12734) * When a post is saved, check for tinymce and save any editors. (#12568) * When a post is saved, check for tinymce and save any editors. * Importing tinymce and using tinyMCE vs the object stored in window.tinymce. * Updated version number and changelog. * no longer importing tinymce since we use the tinyMCE global. tinyMCE.triggerSave works now. checking if tinyMCE exists before making the call just in case. * Using typeof to check for tinyMCE and fixed issues brought up in travis run. * using window.tinyMCE again to avoid warning RE undefined var * Restore the package.json version. * Add e2e tests for the custom wp_editor metaboxes * Rename functions, removing gutenberg_ prefix (#12326) * Rename functions, removing gutenberg_ and prefixing with wp_ * Remove wp_ prefix to match core * Remove function check per review * Annotations: Apply annotation className as string (#12741) * RichText: Ensure instance is selected before setting back selection (#12737) * Fix for #11663 (#12728) * Fixed Deleting an HTML Anchor attribute leaves an empty HTML id attribute * Fixed Deleting an HTML Anchor attribute leaves an empty * Update plugin version to 4.7.0-rc.1 (#12752) * Add an error state to the image block to allow upload errors to display (#10224) * Try: JS Console warning for when in Quirks Mode (#12575) * Try: JS Console warning for when in Quirks Mode This PR detects whether the browser is in Quirks Mode. Quirks Mode is a rendering method used when the doctype definition is missing or incorrectly placed in the HTML source, causing the browser to have difficulty detecting the type of document it is to render. This is usually caused by a PHP error, or even just a style tag that is output incorrectly on the page. See discussion in https://github.com/WordPress/gutenberg/pull/12455 and https://github.com/WordPress/gutenberg/issues/11378. The usual result is Gutenberg rendering incorrectly, notably with metaboxes overlapping content. The purpose of this PR is to help developers debug the issue and fix it at the root. As such, it adds a console warning, props @nickcernis for the text: ``` [Warning] Your browser is using Quirks Mode. This can cause rendering issues such as blocks overlaying meta boxes in the editor. Quirks Mode can be triggered by PHP errors or HTML code appearing before the opening <!DOCTYPE html>. Try checking the raw page source or your site's PHP error log and resolving errors there, removing any HTML before the doctype, or disabling plugins. ``` It also augments the documentation to add a note about this. * Move warning to index.js * Remove try/catch. * Tweak: Remove redundant [warning] in warn call * Organizing screenshot assets for the block tutorial inside the designers-developers directory in the repo (#12745) * Rename backwards compatiblity to backward compatibility (#12751) * Rename backwards compatiblity to backward compatibility * Remove package-lock from commit * Update CONTRIBUTING.md Co-Authored-By: mkaz <marcus@mkaz.com> * Update CONTRIBUTING.md Co-Authored-By: mkaz <marcus@mkaz.com> * Whitespace in manifest * Update node-sass to 4.11.0 to support Node.js 11 (#12541) ## Description Fixes #12539 by updating node-sass to support Node.js 11. ## How has this been tested? Running `npm install` on macOS 10.14 with Node.js 11.2 without problems. ## Types of changes Minor dependency bump to support Node.js 11. ## Checklist: - [x] My code is tested. - [x] My code follows the WordPress code style. <!-- Check code: `npm run lint`, Guidelines: https://make.wordpress.org/core/handbook/best-practices/coding-standards/javascript/ --> - [x] My code follows the accessibility standards. <!-- Guidelines: https://make.wordpress.org/core/handbook/best-practices/coding-standards/accessibility-coding-standards/ --> - [x] My code has proper inline documentation. <!-- Guidelines: https://make.wordpress.org/core/handbook/best-practices/inline-documentation-standards/javascript/ --> * Add attributes to ServerSideRender readme (#12793) * Add attributes to ServerSideRender readme Adds a code example demonstrating how to define attributes when registering a block that will use attributes in a ServerSideRender component. * Add whitespace and inline code markup to ServerSideRender readme Implements requested changes from code review. * Scripts: Add check-engines script to the package (#12721) * Scripts: Add check-engines script to the package * Update packages/scripts/CHANGELOG.md Co-Authored-By: gziolo <grzegorz@gziolo.pl> * Update packages/scripts/README.md Co-Authored-By: gziolo <grzegorz@gziolo.pl> * Update minimal node version to 10.x Co-Authored-By: gziolo <grzegorz@gziolo.pl> * Move devDependencies to root package.json file (#12720) * Chore: Remove unused npm dependencies from the root package.json file * Move devDependencies to root package.json file * Fix php notice from the recent comments block (#12812) * RichText: Fix React warning shown when unmounting a currently selected RichText. (#12817) * Packages: Reimplement ESLint config as plugin (#12763) * Packages: Move eslint-config to eslint-plugin (Fails pre-commit, but in effort to ensure history preservation) * eslint-plugin: Add npmrc to avoid package-lock.json * Framework: Update path references for eslint-config to -plugin * eslint-plugin: Reimplement ESLint config as plugin * eslint-plugin: Unmark as private * eslint-plugin: Undocument custom ruleset * 4.7 (#12819) * Bump plugin version to 4.7.0 * chore(release): publish - @wordpress/annotations@1.0.4 - @wordpress/api-fetch@2.2.6 - @wordpress/block-library@2.2.10 - @wordpress/block-serialization-default-parser@2.0.2 - @wordpress/block-serialization-spec-parser@2.0.2 - @wordpress/blocks@6.0.4 - @wordpress/components@7.0.4 - @wordpress/core-data@2.0.15 - @wordpress/data@4.1.0 - @wordpress/date@3.0.1 - @wordpress/edit-post@3.1.5 - @wordpress/editor@9.0.5 - @wordpress/eslint-plugin@1.0.0 - @wordpress/format-library@1.2.8 - @wordpress/html-entities@2.0.4 - @wordpress/list-reusable-blocks@1.1.17 - @wordpress/notices@1.1.1 - @wordpress/nux@3.0.5 - @wordpress/rich-text@3.0.3 - @wordpress/url@2.3.2 - @wordpress/viewport@2.0.13 * Update changelogs after 4.7 package releases * Add back package-lock.json --- .eslintrc.js | 2 +- docs/manifest.json | 6 +- gutenberg.php | 2 +- package-lock.json | 76 ++++++++++++---- package.json | 31 ++++--- packages/annotations/CHANGELOG.md | 2 + packages/annotations/package.json | 2 +- packages/api-fetch/CHANGELOG.md | 2 + packages/api-fetch/package.json | 2 +- .../package.json | 4 - packages/babel-plugin-makepot/package.json | 4 - packages/block-library/CHANGELOG.md | 2 + packages/block-library/package.json | 7 +- .../CHANGELOG.md | 2 + .../package.json | 5 +- .../CHANGELOG.md | 2 + .../package.json | 5 +- packages/blocks/CHANGELOG.md | 2 + packages/blocks/package.json | 5 +- packages/browserslist-config/package.json | 3 - packages/components/CHANGELOG.md | 2 + packages/components/package.json | 7 +- .../src/server-side-render/README.md | 24 ++++- packages/compose/package.json | 5 -- packages/core-data/CHANGELOG.md | 2 + packages/core-data/package.json | 5 +- .../package.json | 3 - packages/data/CHANGELOG.md | 2 +- packages/data/package.json | 7 +- packages/date/CHANGELOG.md | 2 + packages/date/package.json | 2 +- packages/edit-post/CHANGELOG.md | 2 +- packages/edit-post/package.json | 6 +- packages/editor/CHANGELOG.md | 2 +- packages/editor/package.json | 8 +- .../editor/src/components/rich-text/index.js | 4 + packages/element/package.json | 3 - packages/eslint-config/README.md | 24 ----- packages/eslint-config/configs/es5.js | 12 --- packages/eslint-config/configs/esnext.js | 8 -- packages/eslint-config/configs/rules/es5.js | 84 ------------------ .../eslint-config/configs/rules/esnext.js | 66 -------------- packages/eslint-plugin/.npmrc | 1 + packages/eslint-plugin/CHANGELOG.md | 5 ++ packages/eslint-plugin/README.md | 48 ++++++++++ packages/eslint-plugin/configs/custom.js | 19 ++++ .../index.js => eslint-plugin/configs/es5.js} | 87 ++++--------------- packages/eslint-plugin/configs/esnext.js | 36 ++++++++ packages/eslint-plugin/configs/index.js | 1 + packages/eslint-plugin/configs/jsx-a11y.js | 17 ++++ packages/eslint-plugin/configs/react.js | 28 ++++++ packages/eslint-plugin/configs/recommended.js | 16 ++++ packages/eslint-plugin/index.js | 3 + .../package.json | 12 +-- packages/format-library/CHANGELOG.md | 2 + packages/format-library/package.json | 2 +- packages/hooks/package.json | 3 - packages/html-entities/CHANGELOG.md | 2 + packages/html-entities/package.json | 3 +- packages/i18n/package.json | 3 - packages/is-shallow-equal/package.json | 8 -- .../package.json | 4 - packages/list-reusable-blocks/CHANGELOG.md | 2 + packages/list-reusable-blocks/package.json | 2 +- packages/notices/CHANGELOG.md | 2 + packages/notices/package.json | 5 +- .../npm-package-json-lint-config/package.json | 3 - packages/nux/CHANGELOG.md | 2 + packages/nux/package.json | 5 +- packages/postcss-themes/package.json | 4 +- packages/redux-routine/package.json | 3 - packages/rich-text/CHANGELOG.md | 2 +- packages/rich-text/package.json | 6 +- packages/scripts/CHANGELOG.md | 6 ++ packages/scripts/README.md | 19 ++++ packages/scripts/package.json | 1 + packages/scripts/scripts/check-engines.js | 34 ++++++++ packages/url/CHANGELOG.md | 2 + packages/url/package.json | 2 +- packages/viewport/CHANGELOG.md | 2 + packages/viewport/package.json | 6 +- 81 files changed, 422 insertions(+), 432 deletions(-) delete mode 100644 packages/eslint-config/README.md delete mode 100644 packages/eslint-config/configs/es5.js delete mode 100644 packages/eslint-config/configs/esnext.js delete mode 100644 packages/eslint-config/configs/rules/es5.js delete mode 100644 packages/eslint-config/configs/rules/esnext.js create mode 100644 packages/eslint-plugin/.npmrc create mode 100644 packages/eslint-plugin/CHANGELOG.md create mode 100644 packages/eslint-plugin/README.md create mode 100644 packages/eslint-plugin/configs/custom.js rename packages/{eslint-config/index.js => eslint-plugin/configs/es5.js} (52%) create mode 100644 packages/eslint-plugin/configs/esnext.js create mode 100644 packages/eslint-plugin/configs/index.js create mode 100644 packages/eslint-plugin/configs/jsx-a11y.js create mode 100644 packages/eslint-plugin/configs/react.js create mode 100644 packages/eslint-plugin/configs/recommended.js create mode 100644 packages/eslint-plugin/index.js rename packages/{eslint-config => eslint-plugin}/package.json (69%) create mode 100644 packages/scripts/scripts/check-engines.js diff --git a/.eslintrc.js b/.eslintrc.js index feced45620657..af4bda32427a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,7 +19,7 @@ const majorMinorRegExp = escapeRegExp( version.replace( /\.\d+$/, '' ) ) + '(\\. module.exports = { root: true, extends: [ - '@wordpress/eslint-config', + 'plugin:@wordpress/eslint-plugin/recommended', 'plugin:jest/recommended', ], rules: { diff --git a/docs/manifest.json b/docs/manifest.json index e8925eec9fb9c..dbf955bd2d247 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -498,9 +498,9 @@ "parent": "packages" }, { - "title": "@wordpress/eslint-config", - "slug": "packages-eslint-config", - "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/eslint-config/README.md", + "title": "@wordpress/eslint-plugin", + "slug": "packages-eslint-plugin", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/eslint-plugin/README.md", "parent": "packages" }, { diff --git a/gutenberg.php b/gutenberg.php index 9f9d7b183aeb5..62215b96d48d1 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.7.0-rc.1 + * Version: 4.7.0 * Author: Gutenberg Team * * @package gutenberg diff --git a/package-lock.json b/package-lock.json index 347cbcd533748..1867b41831f79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.7.0-rc.1", + "version": "4.7.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2590,13 +2590,14 @@ "@babel/runtime": "^7.0.0" } }, - "@wordpress/eslint-config": { - "version": "file:packages/eslint-config", + "@wordpress/eslint-plugin": { + "version": "file:packages/eslint-plugin", "dev": true, "requires": { "babel-eslint": "^8.0.3", "eslint-plugin-jsx-a11y": "6.0.2", - "eslint-plugin-react": "7.7.0" + "eslint-plugin-react": "7.7.0", + "requireindex": "^1.2.0" } }, "@wordpress/format-library": { @@ -2732,7 +2733,9 @@ "dev": true, "requires": { "@babel/runtime": "^7.0.0", - "postcss": "^6.0.16" + "autoprefixer": "^8.2.0", + "postcss": "^6.0.16", + "postcss-color-function": "^4.0.1" } }, "@wordpress/redux-routine": { @@ -2763,6 +2766,7 @@ "@wordpress/npm-package-json-lint-config": "file:packages/npm-package-json-lint-config", "babel-eslint": "8.0.3", "chalk": "^2.4.1", + "check-node-version": "^3.1.1", "cross-spawn": "^5.1.0", "eslint": "^4.19.1", "jest": "^23.6.0", @@ -4502,6 +4506,16 @@ "tweetnacl": "^0.14.3" } }, + "benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, "bfj": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.1.tgz", @@ -15094,7 +15108,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -15104,7 +15118,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -15170,7 +15184,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -15203,7 +15217,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -15253,7 +15267,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -16370,6 +16384,12 @@ "find-up": "^2.1.0" } }, + "platform": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", + "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==", + "dev": true + }, "please-upgrade-node": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", @@ -18649,6 +18669,12 @@ } } }, + "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", @@ -18968,7 +18994,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -18988,7 +19014,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -19123,7 +19149,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -19279,6 +19305,24 @@ } } }, + "shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.0.0.tgz", + "integrity": "sha1-UI0YOLPeWQq4dXsBGyXkMJAJRfc=", + "dev": true + }, + "shallow-equals": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-equals/-/shallow-equals-1.0.0.tgz", + "integrity": "sha1-JLdL8cY0wR7Uxxgqbfb7MA3OQ5A=", + "dev": true + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "dev": true + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -20435,12 +20479,6 @@ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, - "symlink-or-copy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symlink-or-copy/-/symlink-or-copy-1.2.0.tgz", - "integrity": "sha512-W31+GLiBmU/ZR02Ii0mVZICuNEN9daZ63xZMPDsYgPgNjMtg+atqLEGI7PPI936jYSQZxoLb/63xos8Adrx4Eg==", - "dev": true - }, "table": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", diff --git a/package.json b/package.json index 432662628f9e2..03a6bdd8f7616 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.7.0-rc.1", + "version": "4.7.0", "private": true, "description": "A new WordPress editor experience", "repository": "git+https://github.com/WordPress/gutenberg.git", @@ -10,10 +10,6 @@ "WordPress", "editor" ], - "engines": { - "node": ">=8.0.0", - "npm": ">=6.0.0" - }, "dependencies": { "@wordpress/a11y": "file:packages/a11y", "@wordpress/annotations": "file:packages/annotations", @@ -56,55 +52,64 @@ }, "devDependencies": { "@babel/core": "7.0.0", + "@babel/plugin-syntax-jsx": "7.0.0", "@babel/runtime-corejs2": "7.0.0", + "@babel/traverse": "7.0.0", "@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/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/eslint-plugin": "file:packages/eslint-plugin", "@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", "@wordpress/npm-package-json-lint-config": "file:packages/npm-package-json-lint-config", "@wordpress/postcss-themes": "file:packages/postcss-themes", "@wordpress/scripts": "file:packages/scripts", - "autoprefixer": "8.2.0", "babel-loader": "8.0.0", + "benchmark": "2.1.4", + "browserslist": "3.2.8", "chalk": "2.4.1", - "check-node-version": "3.1.1", "concurrently": "3.5.0", "copy-webpack-plugin": "4.5.2", "core-js": "2.5.7", "cross-env": "3.2.4", "cssnano": "4.0.3", + "enzyme": "3.7.0", "deasync": "0.1.13", "deep-freeze": "0.0.1", "doctrine": "2.1.0", "eslint-plugin-jest": "21.5.0", "espree": "3.5.4", + "fbjs": "0.8.17", "glob": "7.1.2", "husky": "0.14.3", + "is-plain-obj": "1.1.0", + "is-equal-shallow": "0.1.3", "jest-puppeteer": "3.2.1", + "jsdom": "11.12.0", "lerna": "3.4.3", "lint-staged": "7.2.0", "lodash": "4.17.10", "mkdirp": "0.5.1", "node-sass": "4.11.0", - "path-type": "3.0.0", "pegjs": "0.10.0", "phpegjs": "1.0.0-beta7", - "postcss-color-function": "4.0.1", "puppeteer": "1.6.1", + "react-dom": "16.6.3", "react-test-renderer": "16.6.3", + "redux": "4.0.0", "rimraf": "2.6.2", "rtlcss": "2.4.0", "sass-loader": "6.0.7", - "source-map-loader": "0.2.3", + "shallow-equal": "1.0.0", + "shallow-equals": "1.0.0", + "shallowequal": "1.1.0", "sprintf-js": "1.1.1", + "source-map-loader": "0.2.3", "stylelint": "9.5.0", "stylelint-config-wordpress": "13.1.0", - "symlink-or-copy": "1.2.0", "uuid": "3.3.2", "webpack": "4.8.3", "webpack-bundle-analyzer": "3.0.2", @@ -145,7 +150,7 @@ "prebuild:packages": "npm run clean:packages && lerna run build && cross-env INCLUDE_PACKAGES=babel-plugin-import-jsx-pragma,postcss-themes,jest-console SKIP_JSX_PRAGMA_TRANSFORM=1 node ./bin/packages/build.js", "build:packages": "cross-env EXCLUDE_PACKAGES=babel-plugin-import-jsx-pragma,jest-console,postcss-themes node ./bin/packages/build.js", "build": "npm run build:packages && cross-env NODE_ENV=production webpack", - "check-engines": "check-node-version --package", + "check-engines": "wp-scripts check-engines", "check-licenses": "concurrently \"wp-scripts check-licenses --prod --gpl2\" \"wp-scripts check-licenses --dev\"", "precheck-local-changes": "npm run docs:build", "check-local-changes": "( git diff -U0 | xargs -0 node bin/process-git-diff ) || ( echo \"There are local uncommitted changes after one or both of 'npm install' or 'npm run docs:build'!\" && exit 1 );", diff --git a/packages/annotations/CHANGELOG.md b/packages/annotations/CHANGELOG.md index edceba997fe67..9d8994d0b4861 100644 --- a/packages/annotations/CHANGELOG.md +++ b/packages/annotations/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.0.4 (2018-12-12) + ## 1.0.3 (2018-11-21) ## 1.0.2 (2018-11-20) diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 5a73fb5e31ae4..3e909c7a0852c 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "1.0.3", + "version": "1.0.4", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md index 912d833f3b909..b4c58ed5d9e1a 100644 --- a/packages/api-fetch/CHANGELOG.md +++ b/packages/api-fetch/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.2.6 (2018-12-12) + ## 2.2.5 (2018-11-20) ## 2.2.4 (2018-11-15) diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 3cb6fb3c22979..0c3ab7e8e9bbc 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "2.2.5", + "version": "2.2.6", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json index 5cd00bf163f86..1c2bb7de9bb95 100644 --- a/packages/babel-plugin-import-jsx-pragma/package.json +++ b/packages/babel-plugin-import-jsx-pragma/package.json @@ -28,10 +28,6 @@ "dependencies": { "@babel/runtime": "^7.0.0" }, - "devDependencies": { - "@babel/core": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0" - }, "peerDependencies": { "@babel/core": "^7.0.0" }, diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index e21a023be7d3d..819415f3b9732 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -29,10 +29,6 @@ "gettext-parser": "^1.3.1", "lodash": "^4.17.10" }, - "devDependencies": { - "@babel/core": "^7.0.0", - "@babel/traverse": "^7.0.0" - }, "peerDependencies": { "@babel/core": "^7.0.0" }, diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index e27ca871be384..f784c887fb110 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.2.10 (2018-12-12) + ## 2.2.9 (2018-11-30) ## 2.2.8 (2018-11-30) diff --git a/packages/block-library/package.json b/packages/block-library/package.json index ad9c4514349cc..3f81dcf5dfd3d 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "2.2.9", + "version": "2.2.10", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -40,11 +40,6 @@ "memize": "^1.0.5", "url": "^0.11.0" }, - "devDependencies": { - "deep-freeze": "^0.0.1", - "enzyme": "^3.7.0", - "react-test-renderer": "^16.6.3" - }, "publishConfig": { "access": "public" } diff --git a/packages/block-serialization-default-parser/CHANGELOG.md b/packages/block-serialization-default-parser/CHANGELOG.md index 6c4f4c53b0645..aaa75a2df97c0 100644 --- a/packages/block-serialization-default-parser/CHANGELOG.md +++ b/packages/block-serialization-default-parser/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.2 (2018-12-12) + ## 2.0.1 (2018-11-30) ## 2.0.0 (2018-11-12) diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index 1a235b320cd06..b5482f3d1efd4 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": "2.0.1", + "version": "2.0.2", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,9 +23,6 @@ "dependencies": { "@babel/runtime": "^7.0.0" }, - "devDependencies": { - "@wordpress/block-serialization-spec-parser": "file:../block-serialization-spec-parser" - }, "publishConfig": { "access": "public" } diff --git a/packages/block-serialization-spec-parser/CHANGELOG.md b/packages/block-serialization-spec-parser/CHANGELOG.md index 12ffab6521d47..c2db52c417908 100644 --- a/packages/block-serialization-spec-parser/CHANGELOG.md +++ b/packages/block-serialization-spec-parser/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.2 (2018-12-12) + ## 2.0.1 (2018-11-30) ## 2.0.0 (2018-11-12) diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 251a28045fb95..d7bafbb41539c 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": "2.0.1", + "version": "2.0.2", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -18,9 +18,6 @@ "bugs": { "url": "https://github.com/WordPress/gutenberg/issues" }, - "devDependencies": { - "pegjs": "0.10.0" - }, "publishConfig": { "access": "public" }, diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index 33ca7db882fbd..e268a8260c14f 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -1,3 +1,5 @@ +## 6.0.4 (2018-12-12) + ## 6.0.3 (2018-11-30) ## 6.0.2 (2018-11-21) diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 1a662f7a28626..afe971954c18c 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "6.0.3", + "version": "6.0.4", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -41,9 +41,6 @@ "tinycolor2": "^1.4.1", "uuid": "^3.3.2" }, - "devDependencies": { - "deep-freeze": "^0.0.1" - }, "publishConfig": { "access": "public" } diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index a554d875997e8..928f2c2dffb23 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -21,9 +21,6 @@ "node": ">=8" }, "main": "index.js", - "devDependencies": { - "browserslist": "^3.1.0" - }, "publishConfig": { "access": "public" } diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index fb93a60cd4712..8dfd644e15ea0 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,3 +1,5 @@ +## 7.0.4 (2018-12-12) + ## 7.0.3 (2018-11-30) ## 7.0.2 (2018-11-22) diff --git a/packages/components/package.json b/packages/components/package.json index 6657f37b182b6..2425f1bf79dcb 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "7.0.3", + "version": "7.0.4", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -47,11 +47,6 @@ "tinycolor2": "^1.4.1", "uuid": "^3.3.2" }, - "devDependencies": { - "@wordpress/token-list": "file:../token-list", - "enzyme": "^3.7.0", - "react-test-renderer": "^16.6.3" - }, "publishConfig": { "access": "public" } diff --git a/packages/components/src/server-side-render/README.md b/packages/components/src/server-side-render/README.md index ee18a7f4294ed..55b0be7630dab 100644 --- a/packages/components/src/server-side-render/README.md +++ b/packages/components/src/server-side-render/README.md @@ -32,5 +32,25 @@ Output uses the block's `render_callback` function, set when defining the block. ## API Endpoint -The API endpoint for getting the output for ServerSideRender is `/wp/v2/block-renderer/:block`. It accepts any params, which are used as `attributes` for the block's `render_callback` method. - +The API endpoint for getting the output for ServerSideRender is `/wp/v2/block-renderer/:block`. It will use the block's `render_callback` method. + +If you pass `attributes` to `ServerSideRender`, the block must also be registered and have its attributes defined in PHP. + +```php +register_block_type( + 'core/archives', + array( + 'attributes' => array( + 'showPostCounts' => array( + 'type' => 'boolean', + 'default' => false, + ), + 'displayAsDropdown' => array( + 'type' => 'boolean', + 'default' => false, + ), + ), + 'render_callback' => 'render_block_core_archives', + ) +); +``` diff --git a/packages/compose/package.json b/packages/compose/package.json index dc7a4ea89316e..ace35bcce8ba3 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -26,11 +26,6 @@ "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "lodash": "^4.17.10" }, - "devDependencies": { - "enzyme": "^3.7.0", - "react-dom": "^16.6.3", - "react-test-renderer": "^16.6.3" - }, "publishConfig": { "access": "public" } diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index 98ed42a5473f0..3594be36a67ec 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.15 (2018-12-12) + ## 2.0.14 (2018-11-20) ## 2.0.13 (2018-11-15) diff --git a/packages/core-data/package.json b/packages/core-data/package.json index ad9917d785bad..316ac6907f9ab 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "2.0.14", + "version": "2.0.15", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -29,9 +29,6 @@ "lodash": "^4.17.10", "rememo": "^3.0.0" }, - "devDependencies": { - "deep-freeze": "^0.0.1" - }, "publishConfig": { "access": "public" } diff --git a/packages/custom-templated-path-webpack-plugin/package.json b/packages/custom-templated-path-webpack-plugin/package.json index 8cf29e724b187..f1d922ffd5949 100644 --- a/packages/custom-templated-path-webpack-plugin/package.json +++ b/packages/custom-templated-path-webpack-plugin/package.json @@ -27,9 +27,6 @@ "@babel/runtime": "^7.0.0", "escape-string-regexp": "^1.0.5" }, - "devDependencies": { - "webpack": "^4.8.3" - }, "peerDependencies": { "webpack": "^4.0.0" }, diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index d496bb6162e23..8b90a5d7c526a 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -1,4 +1,4 @@ -## 4.1.0 (Unreleased) +## 4.1.0 (2018-12-12) ### New Feature diff --git a/packages/data/package.json b/packages/data/package.json index 493c9af3bd7be..825ddb8f71295 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "4.0.1", + "version": "4.1.0", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -32,11 +32,6 @@ "redux": "^4.0.0", "turbo-combine-reducers": "^1.0.2" }, - "devDependencies": { - "deep-freeze": "^0.0.1", - "enzyme": "^3.7.0", - "react-test-renderer": "^16.6.3" - }, "publishConfig": { "access": "public" } diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md index e6aa964c70518..54bee67c917a9 100644 --- a/packages/date/CHANGELOG.md +++ b/packages/date/CHANGELOG.md @@ -1,3 +1,5 @@ +## 3.0.1 (2018-12-12) + ## 3.0.0 (2018-11-15) ### Breaking Changes diff --git a/packages/date/package.json b/packages/date/package.json index 847cdf2762ad4..7378306fff28c 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "3.0.0", + "version": "3.0.1", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index e4b0c1ad32c68..092061f4501ef 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.1.5 (Unreleased) +## 3.1.5 (2018-12-12) ### Bug Fixes - Fix saving WYSIWYG Meta Boxes diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 6c98e2826302c..0e0e4d008fd95 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "3.1.4", + "version": "3.1.5", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -42,10 +42,6 @@ "lodash": "^4.17.10", "refx": "^3.0.0" }, - "devDependencies": { - "deep-freeze": "^0.0.1", - "enzyme": "^3.7.0" - }, "publishConfig": { "access": "public" } diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index 69dbe834a4aeb..e06655f6738b6 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -1,4 +1,4 @@ -## 9.0.5 (Unreleased) +## 9.0.5 (2018-12-12) ### Bug Fixes diff --git a/packages/editor/package.json b/packages/editor/package.json index dfe9ac51ed3fd..6850b40372ae2 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "9.0.4", + "version": "9.0.5", "description": "Building blocks for WordPress editors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -59,12 +59,6 @@ "tinymce": "^4.7.2", "traverse": "^0.6.6" }, - "devDependencies": { - "deep-freeze": "^0.0.1", - "enzyme": "^3.7.0", - "react-dom": "^16.6.3", - "react-test-renderer": "^16.6.3" - }, "publishConfig": { "access": "public" } diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index 0731916d6a1a7..bc814d8ebff9c 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -129,6 +129,10 @@ export class RichText extends Component { this.lastHistoryValue = value; } + componentWillUnmount() { + document.removeEventListener( 'selectionchange', this.onSelectionChange ); + } + setRef( node ) { this.editableRef = node; } diff --git a/packages/element/package.json b/packages/element/package.json index 5e80ae9220c0d..19707938c6a67 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -27,9 +27,6 @@ "react": "^16.6.3", "react-dom": "^16.6.3" }, - "devDependencies": { - "enzyme": "^3.7.0" - }, "publishConfig": { "access": "public" } diff --git a/packages/eslint-config/README.md b/packages/eslint-config/README.md deleted file mode 100644 index 5f2b003e765c5..0000000000000 --- a/packages/eslint-config/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# ESLint Config - -[ESLint](https://eslint.org/) config for WordPress development. - -## Installation - -Install the module - -```bash -npm install @wordpress/eslint-config --save-dev -``` - -### Usage - -Next, extend the configuration from your project's `.eslintrc` file: - -```json -"extends": "@wordpress/eslint-config" -``` - -Refer to the [ESLint documentation on Shareable Configs](http://eslint.org/docs/developer-guide/shareable-configs) for more information. - - -<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/eslint-config/configs/es5.js b/packages/eslint-config/configs/es5.js deleted file mode 100644 index 2e56e7a2f5fcd..0000000000000 --- a/packages/eslint-config/configs/es5.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * The original version of this file is based on WordPress ESLint rules and shared configs: - * https://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress. - */ - -module.exports = { - env: { - es6: true, - }, - - rules: require( './rules/esnext' ), -}; diff --git a/packages/eslint-config/configs/esnext.js b/packages/eslint-config/configs/esnext.js deleted file mode 100644 index 3b3a80d7d4cd8..0000000000000 --- a/packages/eslint-config/configs/esnext.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * The original version of this file is based on WordPress ESLint rules and shared configs: - * https://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress. - */ - -module.exports = { - rules: require( './rules/es5' ), -}; diff --git a/packages/eslint-config/configs/rules/es5.js b/packages/eslint-config/configs/rules/es5.js deleted file mode 100644 index 61e3e01c343f1..0000000000000 --- a/packages/eslint-config/configs/rules/es5.js +++ /dev/null @@ -1,84 +0,0 @@ -module.exports = { - // Possible Errors - // Disallow assignment in conditional expressions - 'no-cond-assign': [ 'error', 'except-parens' ], - // Disallow irregular whitespace outside of strings and comments - 'no-irregular-whitespace': 'error', - // Best Practices - // Specify curly brace conventions for all control statements - curly: [ 'error', 'all' ], - // Encourages use of dot notation whenever possible - 'dot-notation': [ 'error', { - allowKeywords: true, - allowPattern: '^[a-z]+(_[a-z]+)+$', - } ], - // Disallow use of multiline strings - 'no-multi-str': 'error', - // Disallow use of the with statement - 'no-with': 'error', - // Requires to declare all vars on top of their containing scope - 'vars-on-top': 'error', - // Require immediate function invocation to be wrapped in parentheses - 'wrap-iife': 'error', - // Require or disallow Yoda conditions - yoda: [ 'error', 'always' ], - // Strict Mode - // Variables - // Stylistic Issues - // Enforce spacing inside array brackets - 'array-bracket-spacing': [ 'error', 'always' ], - // Enforce one true brace style - 'brace-style': 'error', - // Require camel case names - camelcase: [ 'error', { - properties: 'always', - } ], - // Disallow or enforce trailing commas - 'comma-dangle': [ 'error', 'never' ], - // Enforce spacing before and after comma - 'comma-spacing': 'error', - // Enforce one true comma style - 'comma-style': [ 'error', 'last' ], - // Enforce newline at the end of file, with no multiple empty lines - 'eol-last': 'error', - // Enforces spacing between keys and values in object literal properties - 'key-spacing': [ 'error', { - beforeColon: false, - afterColon: true, - } ], - // Enforce spacing before and after keywords - 'keyword-spacing': 'error', - // Disallow mixed "LF" and "CRLF" as linebreaks - 'linebreak-style': [ 'error', 'unix' ], - // Enforces empty lines around comments - 'lines-around-comment': [ 'error', { - beforeLineComment: true, - } ], - // Disallow mixed spaces and tabs for indentation - 'no-mixed-spaces-and-tabs': 'error', - // Disallow multiple empty lines - 'no-multiple-empty-lines': 'error', - // Disallow trailing whitespace at the end of lines - 'no-trailing-spaces': 'error', - // Require or disallow an newline around variable declarations - 'one-var-declaration-per-line': [ 'error', 'initializations' ], - // Enforce operators to be placed before or after line breaks - 'operator-linebreak': [ 'error', 'after' ], - // Specify whether backticks, double or single quotes should be used - quotes: [ 'error', 'single' ], - // Require or disallow use of semicolons instead of ASI - semi: [ 'error', 'always' ], - // Require or disallow space before blocks - 'space-before-blocks': [ 'error', 'always' ], - // Require or disallow space before function opening parenthesis - 'space-before-function-paren': [ 'error', 'never' ], - // Require or disallow space before blocks - 'space-in-parens': [ 'error', 'always', { exceptions: [ '{}', '[]' ] } ], - // Require spaces around operators - 'space-infix-ops': 'error', - // Require or disallow spaces before/after unary operators (words on by default, nonwords) - 'space-unary-ops': [ 'error', { - overrides: { '!': true }, - } ], - // Legacy -}; diff --git a/packages/eslint-config/configs/rules/esnext.js b/packages/eslint-config/configs/rules/esnext.js deleted file mode 100644 index dcfc27f06554f..0000000000000 --- a/packages/eslint-config/configs/rules/esnext.js +++ /dev/null @@ -1,66 +0,0 @@ -// see https://eslint.org/docs/rules/#ecmascript-6 -// -module.exports = { - // require braces around arrow function bodies - 'arrow-body-style': 'off', - // require parentheses around arrow function arguments - 'arrow-parens': 'off', - // enforce consistent spacing before and after the arrow in arrow functions - 'arrow-spacing': 'off', - // require super() calls in constructors - 'constructor-super': 'error', - // enforce consistent spacing around * operators in generator functions - 'generator-star-spacing': 'off', - // disallow reassigning class members - 'no-class-assign': 'off', - // disallow arrow functions where they could be confused with comparisons - 'no-confusing-arrow': 'off', - // disallow reassigning `const` variables - 'no-const-assign': 'error', - // disallow duplicate class members - 'no-dupe-class-members': 'error', - // disallow duplicate module imports - 'no-duplicate-imports': 'error', - // disallow `new` operators with the `Symbol` object - 'no-new-symbol': 'off', - // disallow specified modules when loaded by `import` - 'no-restricted-imports': 'off', - // disallow `this`/`super` before calling `super()` in constructors - 'no-this-before-super': 'off', - // disallow unnecessary computed property keys in object literals - 'no-useless-computed-key': 'error', - // disallow unnecessary constructors - 'no-useless-constructor': 'error', - // disallow renaming import, export, and destructured assignments to the same name - 'no-useless-rename': 'off', - // require `let` or `const` instead of `var` - 'no-var': 'error', - // require or disallow method and property shorthand syntax for object literals - 'object-shorthand': 'off', - // require arrow functions as callbacks - 'prefer-arrow-callback': 'off', - // require `const` declarations for variables that are never reassigned after declared - 'prefer-const': 'error', - // require destructuring from arrays and/or objects - 'prefer-destructuring': 'off', - // disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals - 'prefer-numeric-literals': 'off', - // require rest parameters instead of `arguments` - 'prefer-rest-params': 'off', - // require spread operators instead of `.apply()` - 'prefer-spread': 'off', - // require template literals instead of string concatenation - 'prefer-template': 'off', - // require generator functions to contain `yield` - 'require-yield': 'off', - // enforce spacing between rest and spread operators and their expressions - 'rest-spread-spacing': 'off', - // enforce sorted import declarations within modules - 'sort-imports': 'off', - // require symbol descriptions - 'symbol-description': 'off', - // require or disallow spacing around embedded expressions of template strings - 'template-curly-spacing': [ 'error', 'always' ], - // require or disallow spacing around the `*` in `yield*` expressions - 'yield-star-spacing': 'off', -}; diff --git a/packages/eslint-plugin/.npmrc b/packages/eslint-plugin/.npmrc new file mode 100644 index 0000000000000..43c97e719a5a8 --- /dev/null +++ b/packages/eslint-plugin/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md new file mode 100644 index 0000000000000..3d6a36751ddd1 --- /dev/null +++ b/packages/eslint-plugin/CHANGELOG.md @@ -0,0 +1,5 @@ +## 1.0.0 (2018-12-12) + +### New Features + +- Initial release. diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md new file mode 100644 index 0000000000000..fd516bbf622b4 --- /dev/null +++ b/packages/eslint-plugin/README.md @@ -0,0 +1,48 @@ +# ESLint Plugin + +[ESLint](https://eslint.org/) plugin including configurations and custom rules for WordPress development. + +## Installation + +Install the module + +```bash +npm install @wordpress/eslint-plugin --save-dev +``` + +### Usage + +To opt-in to the default configuration, extend your own project's `.eslintrc` file: + +```json +{ + "extends": [ "plugin:@wordpress/eslint-plugin/recommended" ] +} +``` + +Refer to the [ESLint documentation on Shareable Configs](http://eslint.org/docs/developer-guide/shareable-configs) for more information. + +The `recommended` preset will include rules governing an ES2015+ environment, and includes rules from the [`eslint-plugin-jsx-a11y`](https://github.com/evcohen/eslint-plugin-jsx-a11y) and [`eslint-plugin-react`](https://github.com/yannickcr/eslint-plugin-react) projects. + +#### Rulesets + +Alternatively, you can opt-in to only the more granular rulesets offered by the plugin. These include: + +- `es5` +- `esnext` +- `jsx-a11y` +- `react` + +For example, if your project does not use React, you could consider extending including only the ESNext rules in your project using the following `extends` definition: + +```json +{ + "extends": [ "plugin:@wordpress/eslint-plugin/esnext" ] +} +``` + +These rules can be used additively, so you could extend both `esnext` and `custom` rulesets, but omit the `react` and `jsx-a11y` configurations. + +The granular rulesets will not define any environment globals. As such, if they are required for your project, you will need to define them yourself. + +<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/eslint-plugin/configs/custom.js b/packages/eslint-plugin/configs/custom.js new file mode 100644 index 0000000000000..0318c85c324c9 --- /dev/null +++ b/packages/eslint-plugin/configs/custom.js @@ -0,0 +1,19 @@ +module.exports = { + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: 'CallExpression[callee.name=/^__|_n|_x$/]:not([arguments.0.type=/^Literal|BinaryExpression$/])', + message: 'Translate function arguments must be string literals.', + }, + { + selector: 'CallExpression[callee.name=/^_n|_x$/]:not([arguments.1.type=/^Literal|BinaryExpression$/])', + message: 'Translate function arguments must be string literals.', + }, + { + selector: 'CallExpression[callee.name=_nx]:not([arguments.2.type=/^Literal|BinaryExpression$/])', + message: 'Translate function arguments must be string literals.', + }, + ], + }, +}; diff --git a/packages/eslint-config/index.js b/packages/eslint-plugin/configs/es5.js similarity index 52% rename from packages/eslint-config/index.js rename to packages/eslint-plugin/configs/es5.js index aa02fdc33ff9d..167e542ef69a6 100644 --- a/packages/eslint-config/index.js +++ b/packages/eslint-plugin/configs/es5.js @@ -1,56 +1,26 @@ module.exports = { - parser: 'babel-eslint', - extends: [ - './configs/es5.js', - './configs/esnext.js', - 'plugin:react/recommended', - 'plugin:jsx-a11y/recommended', - ], - env: { - node: true, - }, - parserOptions: { - sourceType: 'module', - ecmaFeatures: { - jsx: true, - }, - }, - globals: { - window: true, - document: true, - }, - plugins: [ - 'react', - 'jsx-a11y', - ], rules: { 'array-bracket-spacing': [ 'error', 'always' ], - 'arrow-parens': [ 'error', 'always' ], - 'arrow-spacing': 'error', 'brace-style': [ 'error', '1tbs' ], - camelcase: [ 'error', { properties: 'never' } ], + camelcase: [ 'error', { + properties: 'never', + } ], 'comma-dangle': [ 'error', 'always-multiline' ], 'comma-spacing': 'error', - 'comma-style': 'error', - 'computed-property-spacing': [ 'error', 'always' ], + 'comma-style': [ 'error', 'last' ], + curly: [ 'error', 'all' ], 'dot-notation': 'error', 'eol-last': 'error', eqeqeq: 'error', 'func-call-spacing': 'error', indent: [ 'error', 'tab', { SwitchCase: 1 } ], - 'jsx-a11y/label-has-for': [ 'error', { - required: 'id', - } ], - 'jsx-a11y/media-has-caption': 'off', - 'jsx-a11y/no-noninteractive-tabindex': 'off', - 'jsx-a11y/role-has-required-aria-props': 'off', - 'jsx-quotes': 'error', 'key-spacing': 'error', 'keyword-spacing': 'error', - 'lines-around-comment': 'off', + 'linebreak-style': [ 'error', 'unix' ], 'no-alert': 'error', 'no-bitwise': 'error', 'no-caller': 'error', + 'no-cond-assign': [ 'error', 'except-parens' ], 'no-console': 'error', 'no-debugger': 'error', 'no-dupe-args': 'error', @@ -60,31 +30,18 @@ module.exports = { 'no-eval': 'error', 'no-extra-semi': 'error', 'no-fallthrough': 'error', + 'no-irregular-whitespace': 'error', 'no-lonely-if': 'error', + 'no-multi-str': 'error', 'no-mixed-operators': 'error', 'no-mixed-spaces-and-tabs': 'error', 'no-multiple-empty-lines': [ 'error', { max: 1 } ], 'no-multi-spaces': 'error', - 'no-multi-str': 'off', 'no-negated-in-lhs': 'error', 'no-nested-ternary': 'error', 'no-redeclare': 'error', - 'no-restricted-syntax': [ - 'error', - { - selector: 'CallExpression[callee.name=/^__|_n|_x$/]:not([arguments.0.type=/^Literal|BinaryExpression$/])', - message: 'Translate function arguments must be string literals.', - }, - { - selector: 'CallExpression[callee.name=/^_n|_x$/]:not([arguments.1.type=/^Literal|BinaryExpression$/])', - message: 'Translate function arguments must be string literals.', - }, - { - selector: 'CallExpression[callee.name=_nx]:not([arguments.2.type=/^Literal|BinaryExpression$/])', - message: 'Translate function arguments must be string literals.', - }, - ], 'no-shadow': 'error', + 'no-trailing-spaces': 'error', 'no-undef': 'error', 'no-undef-init': 'error', 'no-unreachable': 'error', @@ -93,23 +50,13 @@ module.exports = { 'no-unused-vars': 'error', 'no-useless-return': 'error', 'no-whitespace-before-property': 'error', + 'no-with': 'error', 'object-curly-spacing': [ 'error', 'always' ], + 'one-var-declaration-per-line': [ 'error', 'initializations' ], + 'operator-linebreak': [ 'error', 'after' ], 'padded-blocks': [ 'error', 'never' ], - quotes: [ 'error', 'single', { allowTemplateLiterals: true, avoidEscape: true } ], 'quote-props': [ 'error', 'as-needed' ], - 'react/display-name': 'off', - 'react/jsx-curly-spacing': [ 'error', { - when: 'always', - children: true, - } ], - 'react/jsx-equals-spacing': 'error', - 'react/jsx-indent': [ 'error', 'tab' ], - 'react/jsx-indent-props': [ 'error', 'tab' ], - 'react/jsx-key': 'error', - 'react/jsx-tag-spacing': 'error', - 'react/no-children-prop': 'off', - 'react/prop-types': 'off', - 'react/react-in-jsx-scope': 'off', + quotes: [ 'error', 'single', { avoidEscape: true } ], semi: 'error', 'semi-spacing': 'error', 'space-before-blocks': [ 'error', 'always' ], @@ -119,11 +66,10 @@ module.exports = { asyncArrow: 'always', } ], 'space-in-parens': [ 'error', 'always' ], - 'space-infix-ops': [ 'error', { int32Hint: false } ], + 'space-infix-ops': 'error', 'space-unary-ops': [ 'error', { overrides: { '!': true, - yield: true, }, } ], 'valid-jsdoc': [ 'error', { @@ -151,6 +97,7 @@ module.exports = { requireReturn: false, } ], 'valid-typeof': 'error', - yoda: 'off', + 'vars-on-top': 'error', + 'wrap-iife': 'error', }, }; diff --git a/packages/eslint-plugin/configs/esnext.js b/packages/eslint-plugin/configs/esnext.js new file mode 100644 index 0000000000000..7e01f959fde72 --- /dev/null +++ b/packages/eslint-plugin/configs/esnext.js @@ -0,0 +1,36 @@ +module.exports = { + env: { + es6: true, + }, + extends: [ + require.resolve( './es5.js' ), + ], + parserOptions: { + sourceType: 'module', + }, + rules: { + // Disable ES5-specific (extended from ES5) + 'vars-on-top': 'off', + + // Enable ESNext-specific + 'arrow-parens': [ 'error', 'always' ], + 'arrow-spacing': 'error', + 'computed-property-spacing': [ 'error', 'always' ], + 'constructor-super': 'error', + 'no-const-assign': 'error', + 'no-dupe-class-members': 'error', + 'no-duplicate-imports': 'error', + 'no-useless-computed-key': 'error', + 'no-useless-constructor': 'error', + 'no-var': 'error', + 'prefer-const': 'error', + quotes: [ 'error', 'single', { allowTemplateLiterals: true, avoidEscape: true } ], + 'space-unary-ops': [ 'error', { + overrides: { + '!': true, + yield: true, + }, + } ], + 'template-curly-spacing': [ 'error', 'always' ], + }, +}; diff --git a/packages/eslint-plugin/configs/index.js b/packages/eslint-plugin/configs/index.js new file mode 100644 index 0000000000000..035c09a8fa767 --- /dev/null +++ b/packages/eslint-plugin/configs/index.js @@ -0,0 +1 @@ +module.exports = require( 'requireindex' )( __dirname ); diff --git a/packages/eslint-plugin/configs/jsx-a11y.js b/packages/eslint-plugin/configs/jsx-a11y.js new file mode 100644 index 0000000000000..38dd0ed2a3bf0 --- /dev/null +++ b/packages/eslint-plugin/configs/jsx-a11y.js @@ -0,0 +1,17 @@ +module.exports = { + extends: [ + 'plugin:jsx-a11y/recommended', + ], + plugins: [ + 'jsx-a11y', + ], + rules: { + 'jsx-a11y/label-has-for': [ 'error', { + required: 'id', + } ], + 'jsx-a11y/media-has-caption': 'off', + 'jsx-a11y/no-noninteractive-tabindex': 'off', + 'jsx-a11y/role-has-required-aria-props': 'off', + 'jsx-quotes': 'error', + }, +}; diff --git a/packages/eslint-plugin/configs/react.js b/packages/eslint-plugin/configs/react.js new file mode 100644 index 0000000000000..05c09b7e16809 --- /dev/null +++ b/packages/eslint-plugin/configs/react.js @@ -0,0 +1,28 @@ +module.exports = { + extends: [ + 'plugin:react/recommended', + ], + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + plugins: [ + 'react', + ], + rules: { + 'react/display-name': 'off', + 'react/jsx-curly-spacing': [ 'error', { + when: 'always', + children: true, + } ], + 'react/jsx-equals-spacing': 'error', + 'react/jsx-indent': [ 'error', 'tab' ], + 'react/jsx-indent-props': [ 'error', 'tab' ], + 'react/jsx-key': 'error', + 'react/jsx-tag-spacing': 'error', + 'react/no-children-prop': 'off', + 'react/prop-types': 'off', + 'react/react-in-jsx-scope': 'off', + }, +}; diff --git a/packages/eslint-plugin/configs/recommended.js b/packages/eslint-plugin/configs/recommended.js new file mode 100644 index 0000000000000..370355bd1d6ad --- /dev/null +++ b/packages/eslint-plugin/configs/recommended.js @@ -0,0 +1,16 @@ +module.exports = { + parser: 'babel-eslint', + extends: [ + require.resolve( './jsx-a11y.js' ), + require.resolve( './react.js' ), + require.resolve( './custom.js' ), + require.resolve( './esnext.js' ), + ], + env: { + node: true, + }, + globals: { + window: true, + document: true, + }, +}; diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js new file mode 100644 index 0000000000000..0933aba1cc826 --- /dev/null +++ b/packages/eslint-plugin/index.js @@ -0,0 +1,3 @@ +module.exports = { + configs: require( './configs' ), +}; diff --git a/packages/eslint-config/package.json b/packages/eslint-plugin/package.json similarity index 69% rename from packages/eslint-config/package.json rename to packages/eslint-plugin/package.json index 8d5c6c93c35ee..13fbae86bfd68 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-plugin/package.json @@ -1,15 +1,14 @@ { - "name": "@wordpress/eslint-config", - "private": true, - "version": "1.0.0-alpha.0", - "description": "ESLint config for WordPress development.", + "name": "@wordpress/eslint-plugin", + "version": "1.0.0", + "description": "ESLint plugin for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", "keywords": [ "wordpress", "eslint" ], - "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/eslint-config/README.md", + "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/eslint-plugin/README.md", "repository": { "type": "git", "url": "https://github.com/WordPress/gutenberg.git" @@ -20,7 +19,8 @@ "dependencies": { "babel-eslint": "^8.0.3", "eslint-plugin-jsx-a11y": "6.0.2", - "eslint-plugin-react": "7.7.0" + "eslint-plugin-react": "7.7.0", + "requireindex": "^1.2.0" }, "publishConfig": { "access": "public" diff --git a/packages/format-library/CHANGELOG.md b/packages/format-library/CHANGELOG.md index cab02a2104311..730914fc9ebd8 100644 --- a/packages/format-library/CHANGELOG.md +++ b/packages/format-library/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.2.8 (2018-12-12) + ## 1.2.7 (2018-11-30) ## 1.2.6 (2018-11-30) diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 9c78a6ba2eedc..bd41d3742ddbc 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "1.2.7", + "version": "1.2.8", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 8f1eb12b94ab1..cef3b444b83d7 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -22,9 +22,6 @@ "dependencies": { "@babel/runtime": "^7.0.0" }, - "devDependencies": { - "benchmark": "^2.1.4" - }, "publishConfig": { "access": "public" } diff --git a/packages/html-entities/CHANGELOG.md b/packages/html-entities/CHANGELOG.md index bc270815c38c8..f9355f9f96865 100644 --- a/packages/html-entities/CHANGELOG.md +++ b/packages/html-entities/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.2 (2018-12-12) + ## 2.0.1 (2018-11-21) ## 2.0.0 (2018-09-05) diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index db944aa08cffe..94fccaa33295c 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/html-entities", - "version": "2.0.3", + "version": "2.0.4", "description": "HTML entity utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -20,7 +20,6 @@ }, "main": "build/index.js", "module": "build-module/index.js", - "react-native": "src/index", "dependencies": { "@babel/runtime": "^7.0.0" }, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 18158a6f0162e..a1005eac73e6e 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -30,9 +30,6 @@ "sprintf-js": "^1.1.1", "tannin": "^1.0.1" }, - "devDependencies": { - "benchmark": "^2.1.4" - }, "publishConfig": { "access": "public" } diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 290b2810f4551..f5553ceb41a47 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -26,14 +26,6 @@ "dependencies": { "@babel/runtime": "^7.0.0" }, - "devDependencies": { - "benchmark": "^2.1.4", - "fbjs": "^0.8.16", - "is-equal-shallow": "^0.1.3", - "shallow-equal": "^1.0.0", - "shallow-equals": "^1.0.0", - "shallowequal": "^1.0.2" - }, "publishConfig": { "access": "public" } diff --git a/packages/library-export-default-webpack-plugin/package.json b/packages/library-export-default-webpack-plugin/package.json index 00b91c6dbeb44..52d28f6e9e184 100644 --- a/packages/library-export-default-webpack-plugin/package.json +++ b/packages/library-export-default-webpack-plugin/package.json @@ -27,10 +27,6 @@ "lodash": "^4.17.10", "webpack-sources": "^1.1.0" }, - "devDependencies": { - "rimraf": "^2.6.2", - "webpack": "^4.8.3" - }, "peerDependencies": { "webpack": "^4.0.0" }, diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md index 8e8526a7f98e6..dd73de16abca4 100644 --- a/packages/list-reusable-blocks/CHANGELOG.md +++ b/packages/list-reusable-blocks/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.1.17 (2018-12-12) + ## 1.1.16 (2018-11-30) ## 1.1.15 (2018-11-22) diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 8c455e8a45812..2ddb7b4afdf53 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.1.16", + "version": "1.1.17", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md index 3699727ec708e..76f6ad9cadaaa 100644 --- a/packages/notices/CHANGELOG.md +++ b/packages/notices/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.1.1 (2018-12-12) + ## 1.1.0 (2018-11-20) ### New Feature diff --git a/packages/notices/package.json b/packages/notices/package.json index f7ae5027587cd..c7b0b8c623294 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "1.1.0", + "version": "1.1.1", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,9 +24,6 @@ "@wordpress/data": "file:../data", "lodash": "^4.17.10" }, - "devDependencies": { - "deep-freeze": "^0.0.1" - }, "publishConfig": { "access": "public" } diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json index e7912c2c2f2cd..ddf65a0f15c8c 100644 --- a/packages/npm-package-json-lint-config/package.json +++ b/packages/npm-package-json-lint-config/package.json @@ -18,9 +18,6 @@ "url": "https://github.com/WordPress/gutenberg/issues" }, "main": "index.js", - "devDependencies": { - "is-plain-obj": "^1.1.0" - }, "peerDependencies": { "npm-package-json-lint": ">= 3.3.1" }, diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md index ffe9ba7ca937e..41aaa82bc4ced 100644 --- a/packages/nux/CHANGELOG.md +++ b/packages/nux/CHANGELOG.md @@ -1,3 +1,5 @@ +## 3.0.5 (2018-12-12) + ## 3.0.4 (2018-11-30) ## 3.0.3 (2018-11-22) diff --git a/packages/nux/package.json b/packages/nux/package.json index d754dc9f891f5..c9db7b58c0fe0 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/nux", - "version": "3.0.4", + "version": "3.0.5", "description": "NUX (New User eXperience) module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -29,9 +29,6 @@ "lodash": "^4.17.10", "rememo": "^3.0.0" }, - "devDependencies": { - "enzyme": "^3.7.0" - }, "publishConfig": { "access": "public" } diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json index 947039a78a946..88727a2f2fe09 100644 --- a/packages/postcss-themes/package.json +++ b/packages/postcss-themes/package.json @@ -27,7 +27,9 @@ "main": "build/index.js", "dependencies": { "@babel/runtime": "^7.0.0", - "postcss": "^6.0.16" + "autoprefixer": "^8.2.0", + "postcss": "^6.0.16", + "postcss-color-function": "^4.0.1" }, "publishConfig": { "access": "public" diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 5eef2653d0782..bd28bcde065c7 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -26,9 +26,6 @@ "is-promise": "^2.1.0", "rungen": "^0.3.2" }, - "devDependencies": { - "redux": "^4.0.0" - }, "publishConfig": { "access": "public" } diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md index aadcae6d60799..38b48d8af8766 100644 --- a/packages/rich-text/CHANGELOG.md +++ b/packages/rich-text/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.0.3 (Unreleased) +## 3.0.3 (2018-12-12) ### Internal diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index aeb67dbfa3506..18b5352a78a91 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "3.0.2", + "version": "3.0.3", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -27,10 +27,6 @@ "lodash": "^4.17.10", "rememo": "^3.0.0" }, - "devDependencies": { - "deep-freeze": "^0.0.1", - "jsdom": "^11.12.0" - }, "publishConfig": { "access": "public" } diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index c07fe9a2e6d56..6f2977dc07f90 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.5.0 (Unreleased) + +### New Feature + +- Added support for `check-engines` script ([#12721](https://github.com/WordPress/gutenberg/pull/12721)) + ## 2.4.4 (2018-11-20) ## 2.4.3 (2018-11-09) diff --git a/packages/scripts/README.md b/packages/scripts/README.md index 4126cdd34cc9b..04ddf958782e0 100644 --- a/packages/scripts/README.md +++ b/packages/scripts/README.md @@ -19,6 +19,7 @@ _Example:_ ```json { "scripts": { + "check-engines": "wp-scripts check-engines", "lint:pkg-json": "wp-scripts lint-pkg-json .", "test": "wp-scripts test-unit-js" } @@ -27,6 +28,24 @@ _Example:_ ## Available Scripts +### `check-engines` + +Check if the current `node`, `npm` (or `yarn`) versions match the given [semantic version](https://semver.org/) ranges. If the given version is not satisfied, information about installing the needed version is printed and the program exits with an error code. It uses [check-node-version](https://www.npmjs.com/package/check-node-version) behind the scenes with the default configuration provided. You can specify your own ranges as described in [check-node-version docs](https://www.npmjs.com/package/check-node-version). + +_Example:_ + +```json +{ + "scripts": { + "check-engines": "wp-scripts check-engines" + } +} +``` + +This is how you execute the script with presented setup: +* `npm run check-engines` - checks installed version of `node` and `npm`. + + ### `wp-scripts lint-js` Helps enforce coding style guidelines for your JavaScript files. It uses [eslint](https://eslint.org/) with no rules provided (we plan to add zero config support in the near future). You can specify your own rules as described in [eslint docs](https://eslint.org/docs/rules/). diff --git a/packages/scripts/package.json b/packages/scripts/package.json index fbbbff832e736..6a0bc5e1986a9 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -36,6 +36,7 @@ "@wordpress/npm-package-json-lint-config": "file:../npm-package-json-lint-config", "babel-eslint": "8.0.3", "chalk": "^2.4.1", + "check-node-version": "^3.1.1", "cross-spawn": "^5.1.0", "eslint": "^4.19.1", "jest": "^23.6.0", diff --git a/packages/scripts/scripts/check-engines.js b/packages/scripts/scripts/check-engines.js new file mode 100644 index 0000000000000..5440e27ccaffb --- /dev/null +++ b/packages/scripts/scripts/check-engines.js @@ -0,0 +1,34 @@ +/** + * External dependencies + */ +const { sync: spawn } = require( 'cross-spawn' ); +const { sync: resolveBin } = require( 'resolve-bin' ); + +/** + * Internal dependencies + */ +const { + getCliArgs, + hasCliArg, +} = require( '../utils' ); + +const args = getCliArgs(); + +const hasConfig = hasCliArg( '--package' ) || + hasCliArg( '--node' ) || + hasCliArg( '--npm' ) || + hasCliArg( '--yarn' ); +const config = ! hasConfig ? + [ + '--node', '>=10.0.0', + '--npm', '>=6.0.0', + ] : + []; + +const result = spawn( + resolveBin( 'check-node-version' ), + [ ...config, ...args ], + { stdio: 'inherit' } +); + +process.exit( result.status ); diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md index 984fb0428eb28..1b5f99789bbf8 100644 --- a/packages/url/CHANGELOG.md +++ b/packages/url/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.3.2 (2018-12-12) + ## 2.3.1 (2018-11-20) ### Bug fixes diff --git a/packages/url/package.json b/packages/url/package.json index 197f2726a6413..43b919a19eb78 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "2.3.1", + "version": "2.3.2", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md index 0dd9a1369466c..62418c8307350 100644 --- a/packages/viewport/CHANGELOG.md +++ b/packages/viewport/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.13 (2018-12-12) + ## 2.0.12 (2018-11-20) ## 2.0.11 (2018-11-15) diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 11da1c485c9d7..e74015bd91349 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "2.0.12", + "version": "2.0.13", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -26,10 +26,6 @@ "@wordpress/element": "file:../element", "lodash": "^4.17.10" }, - "devDependencies": { - "deep-freeze": "^0.0.1", - "react-test-renderer": "^16.6.3" - }, "publishConfig": { "access": "public" } From d9fe45ed6251486f213daf3fc9a7568e2b4977c5 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Fri, 14 Dec 2018 22:23:52 +0100 Subject: [PATCH 12/15] Remove the call that does `blur` the undelying native component when deselecting RichText or PlainText. (#12886) --- packages/editor/src/components/plain-text/index.native.js | 6 ------ packages/editor/src/components/rich-text/index.native.js | 2 -- 2 files changed, 8 deletions(-) diff --git a/packages/editor/src/components/plain-text/index.native.js b/packages/editor/src/components/plain-text/index.native.js index 2373f85ea5815..f7b923b5b976f 100644 --- a/packages/editor/src/components/plain-text/index.native.js +++ b/packages/editor/src/components/plain-text/index.native.js @@ -21,12 +21,6 @@ export default class PlainText extends Component { } } - componentDidUpdate( prevProps ) { - if ( ! this.props.isSelected && prevProps.isSelected ) { - this._input.blur(); - } - } - focus() { this._input.focus(); } diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js index 9ca46d027e2e4..fa1a391e28dbb 100644 --- a/packages/editor/src/components/rich-text/index.native.js +++ b/packages/editor/src/components/rich-text/index.native.js @@ -292,8 +292,6 @@ export class RichText extends Component { componentDidUpdate( prevProps ) { if ( this.props.isSelected && ! prevProps.isSelected ) { this._editor.focus(); - } else if ( ! this.props.isSelected && prevProps.isSelected ) { - this._editor.blur(); } } From 06f3cd5a7a295b747e6a6c84c9aa83a0cf7b8138 Mon Sep 17 00:00:00 2001 From: etoledom <etoledom@icloud.com> Date: Mon, 17 Dec 2018 18:42:14 -0300 Subject: [PATCH 13/15] [rnmobile]: Send blockType prop to RNAztecView (#12869) --- packages/editor/src/components/rich-text/index.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js index fa1a391e28dbb..45c555b233e62 100644 --- a/packages/editor/src/components/rich-text/index.native.js +++ b/packages/editor/src/components/rich-text/index.native.js @@ -370,6 +370,7 @@ export class RichText extends Component { onContentSizeChange={ this.onContentSizeChange } onActiveFormatsChange={ this.onActiveFormatsChange } isSelected={ this.props.isSelected } + blockType={ { tag: tagName } } color={ 'black' } maxImagesWidth={ 200 } style={ style } From b208ea48e4ed4395b9bb668409590816d8c3f437 Mon Sep 17 00:00:00 2001 From: Pinar Olguc <pinarolguc@gmail.com> Date: Tue, 18 Dec 2018 15:30:15 +0300 Subject: [PATCH 14/15] Revert "Remove the call that does `blur` the undelying native component (#12946) * Revert "Remove the call that does `blur` the undelying native component when deselecting RichText or PlainText. (#12886)" This reverts commit d9fe45ed6251486f213daf3fc9a7568e2b4977c5. * Blur only if it is iOS * Fix imports --- .../editor/src/components/plain-text/index.native.js | 10 +++++++++- .../editor/src/components/rich-text/index.native.js | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/components/plain-text/index.native.js b/packages/editor/src/components/plain-text/index.native.js index f7b923b5b976f..ec5fb80247101 100644 --- a/packages/editor/src/components/plain-text/index.native.js +++ b/packages/editor/src/components/plain-text/index.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { TextInput } from 'react-native'; +import { TextInput, Platform } from 'react-native'; /** * WordPress dependencies @@ -14,6 +14,8 @@ import { Component } from '@wordpress/element'; import styles from './style.scss'; export default class PlainText extends Component { + isIOS: boolean = Platform.OS === 'ios'; + componentDidMount() { // if isSelected is true, we should request the focus on this TextInput if ( ( this._input.isFocused() === false ) && ( this._input.props.isSelected === true ) ) { @@ -21,6 +23,12 @@ export default class PlainText extends Component { } } + componentDidUpdate( prevProps ) { + if ( ! this.props.isSelected && prevProps.isSelected && this.isIOS ) { + this._input.blur(); + } + } + focus() { this._input.focus(); } diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js index 45c555b233e62..11d3ec44cf35f 100644 --- a/packages/editor/src/components/rich-text/index.native.js +++ b/packages/editor/src/components/rich-text/index.native.js @@ -2,7 +2,7 @@ * External dependencies */ import RCTAztecView from 'react-native-aztec'; -import { View } from 'react-native'; +import { View, Platform } from 'react-native'; import { forEach, merge, @@ -64,6 +64,8 @@ export function getFormatValue( formatName ) { } export class RichText extends Component { + isIOS: boolean = Platform.OS === 'ios'; + constructor() { super( ...arguments ); this.onChange = this.onChange.bind( this ); @@ -292,6 +294,8 @@ export class RichText extends Component { componentDidUpdate( prevProps ) { if ( this.props.isSelected && ! prevProps.isSelected ) { this._editor.focus(); + } else if ( ! this.props.isSelected && prevProps.isSelected && this.isIOS ) { + this._editor.blur(); } } From f0618158f35bd362a61021765242594a7628c823 Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler <dekervit@gmail.com> Date: Wed, 19 Dec 2018 10:38:07 +0100 Subject: [PATCH 15/15] Fix lint errors in the mobile branch (#12990) * Revert changes to lib/load.php * Fix lint errors in More * Fix lint error for PlainText * Fix lint error in RichText --- lib/load.php | 5 ++++- packages/block-library/src/more/edit.native.js | 8 ++++---- packages/editor/src/components/plain-text/index.native.js | 5 ++++- packages/editor/src/components/rich-text/index.native.js | 3 +-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/load.php b/lib/load.php index 0c55db3a37f6c..ee1c973f99568 100644 --- a/lib/load.php +++ b/lib/load.php @@ -62,7 +62,10 @@ if ( ! function_exists( 'render_block_core_categories' ) ) { require dirname( __FILE__ ) . '/../packages/block-library/src/categories/index.php'; } -if ( ! function_exists( 'render_block_core_latest_comments' ) ) { +// Currently merged in core as `gutenberg_render_block_core_latest_comments`, +// expected to change soon. +if ( ! function_exists( 'render_block_core_latest_comments' ) + && ! function_exists( 'gutenberg_render_block_core_latest_comments' ) ) { require dirname( __FILE__ ) . '/../packages/block-library/src/latest-comments/index.php'; } if ( ! function_exists( 'render_block_core_latest_posts' ) ) { diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index 2a17104d3077b..6a73a44c73b6a 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { View, Text } from 'react-native'; +import { View } from 'react-native'; /** * WordPress dependencies @@ -48,7 +48,7 @@ export default class MoreEdit extends Component { } renderLine() { - return <View style={ styles[ 'block-library-more__line' ] } /> + return <View style={ styles[ 'block-library-more__line' ] } />; } renderText() { @@ -71,12 +71,12 @@ export default class MoreEdit extends Component { onBlur={ onBlur } /> </View> - ) + ); } render() { return ( - <View style={ styles[ 'block-library-more__container' ]}> + <View style={ styles[ 'block-library-more__container' ] }> { this.renderLine() } { this.renderText() } { this.renderLine() } diff --git a/packages/editor/src/components/plain-text/index.native.js b/packages/editor/src/components/plain-text/index.native.js index ec5fb80247101..35ffd2782a133 100644 --- a/packages/editor/src/components/plain-text/index.native.js +++ b/packages/editor/src/components/plain-text/index.native.js @@ -14,7 +14,10 @@ import { Component } from '@wordpress/element'; import styles from './style.scss'; export default class PlainText extends Component { - isIOS: boolean = Platform.OS === 'ios'; + constructor() { + super( ...arguments ); + this.isIOS = Platform.OS === 'ios'; + } componentDidMount() { // if isSelected is true, we should request the focus on this TextInput diff --git a/packages/editor/src/components/rich-text/index.native.js b/packages/editor/src/components/rich-text/index.native.js index 11d3ec44cf35f..f6796a97fdbaa 100644 --- a/packages/editor/src/components/rich-text/index.native.js +++ b/packages/editor/src/components/rich-text/index.native.js @@ -64,10 +64,9 @@ export function getFormatValue( formatName ) { } export class RichText extends Component { - isIOS: boolean = Platform.OS === 'ios'; - constructor() { super( ...arguments ); + this.isIOS = Platform.OS === 'ios'; this.onChange = this.onChange.bind( this ); this.onEnter = this.onEnter.bind( this ); this.onBackspace = this.onBackspace.bind( this );