From 014896535a8a9a2d9a3f6635c71774b1860e9599 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Mon, 28 Jan 2019 13:52:01 -0500 Subject: [PATCH 01/36] Plugin: Remove TinyMCE-specific vendor script handling (#13538) * Plugin: Remove TinyMCE-specific vendor script handling * Plugin: Remove TinyMCE-specific vendor script tests --- lib/client-assets.php | 37 +++++++------------ phpunit/class-vendor-script-filename-test.php | 20 ---------- 2 files changed, 13 insertions(+), 44 deletions(-) diff --git a/lib/client-assets.php b/lib/client-assets.php index c90cb8a05a8ff6..4fff23ed4bdf5f 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -599,32 +599,21 @@ function gutenberg_register_vendor_scripts() { * @since 0.1.0 */ function gutenberg_vendor_script_filename( $handle, $src ) { - $match_tinymce_plugin = preg_match( - '@tinymce.*/plugins/([^/]+)/plugin(\.min)?\.js$@', - $src, - $tinymce_plugin_pieces + $filename = basename( $src ); + $match = preg_match( + '/^' + . '(?P.*?)' + . '(?P\.min)?' + . '(?P\.js)' + . '(?P.*)' + . '$/', + $filename, + $filename_pieces ); - if ( $match_tinymce_plugin ) { - $prefix = 'tinymce-plugin-' . $tinymce_plugin_pieces[1]; - $suffix = isset( $tinymce_plugin_pieces[2] ) ? $tinymce_plugin_pieces[2] : ''; - } else { - $filename = basename( $src ); - $match = preg_match( - '/^' - . '(?P.*?)' - . '(?P\.min)?' - . '(?P\.js)' - . '(?P.*)' - . '$/', - $filename, - $filename_pieces - ); - - $prefix = $handle; - $suffix = $match ? $filename_pieces['suffix'] : ''; - } - $hash = substr( md5( $src ), 0, 8 ); + $prefix = $handle; + $suffix = $match ? $filename_pieces['suffix'] : ''; + $hash = substr( md5( $src ), 0, 8 ); return "${prefix}${suffix}.${hash}.js"; } diff --git a/phpunit/class-vendor-script-filename-test.php b/phpunit/class-vendor-script-filename-test.php index f053df2011159b..0ee7a7b78b83d4 100644 --- a/phpunit/class-vendor-script-filename-test.php +++ b/phpunit/class-vendor-script-filename-test.php @@ -19,16 +19,6 @@ function vendor_script_filename_cases() { 'https://unpkg.com/react-dom@16.6.3/umd/react-dom.development.js', 'react-dom-handle.HASH.js', ), - array( - 'tinymce-handle', - 'https://fiddle.azurewebsites.net/tinymce/nightly/tinymce.js', - 'tinymce-handle.HASH.js', - ), - array( - 'tinymce-plugin-handle', - 'https://fiddle.azurewebsites.net/tinymce/nightly/plugins/lists/plugin.js', - 'tinymce-plugin-lists.HASH.js', - ), // Production mode scripts. array( 'react-handle', @@ -40,16 +30,6 @@ function vendor_script_filename_cases() { 'https://unpkg.com/react-dom@16.6.3/umd/react-dom.production.min.js', 'react-dom-handle.min.HASH.js', ), - array( - 'tinymce-handle', - 'https://fiddle.azurewebsites.net/tinymce/nightly/tinymce.min.js', - 'tinymce-handle.min.HASH.js', - ), - array( - 'tinymce-plugin-handle', - 'https://fiddle.azurewebsites.net/tinymce/nightly/plugins/lists/plugin.min.js', - 'tinymce-plugin-lists.min.HASH.js', - ), // Other cases. array( 'something-handle', From 98b1350b471cb9f3764181ad9a85f262b527df68 Mon Sep 17 00:00:00 2001 From: Karol Gorski Date: Mon, 28 Jan 2019 20:13:28 +0100 Subject: [PATCH 02/36] Update isShallowEqual documentation and unit tests by deep comparison (#13526) * Update isShallowEqual documentation and unit tests by deep comparison * Update packages/is-shallow-equal/README.md Co-Authored-By: Naerriel --- packages/is-shallow-equal/README.md | 18 ++++++++++++++++++ packages/is-shallow-equal/test/index.js | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/is-shallow-equal/README.md b/packages/is-shallow-equal/README.md index ac68c2997ac73c..3c53ed3261e445 100644 --- a/packages/is-shallow-equal/README.md +++ b/packages/is-shallow-equal/README.md @@ -29,6 +29,24 @@ import { isShallowEqualArrays } from '@wordpress/is-shallow-equal'; import { isShallowEqualObjects } from '@wordpress/is-shallow-equal'; ``` +Shallow comparison differs from deep comparison by the fact that it compares members from each as being strictly equal to the other, meaning that arrays and objects will be compared by their _references_, not by their values (see also [_Object Equality in JavaScript_.](http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html)) In situations where nested objects must be compared by value, consider using [Lodash's `isEqual`](https://lodash.com/docs/4.17.11#isEqual) instead. + +```js +import isShallowEqual from '@wordpress/is-shallow-equal'; +import { isEqual } from 'lodash'; // deep comparison + +let object = { a: 1 }; + +isShallowEqual( [ { a: 1 } ], [ { a: 1 } ] ); +// ⇒ false + +isEqual( [ { a: 1 } ], [ { a: 1 } ] ); +// ⇒ true + +isShallowEqual( [ object ], [ object ] ); +// ⇒ true +``` + ## Rationale Shallow equality utilities are already a dime a dozen. Since these operations are often at the core of critical hot code paths, the WordPress contributors had specific requirements that were found to only be partially satisfied by existing solutions. diff --git a/packages/is-shallow-equal/test/index.js b/packages/is-shallow-equal/test/index.js index ee918194cdad14..49f2526dbdf279 100644 --- a/packages/is-shallow-equal/test/index.js +++ b/packages/is-shallow-equal/test/index.js @@ -84,6 +84,14 @@ describe( 'isShallowEqual', () => { expect( isShallowEqual( b, a ) ).toBe( true ); } ); + it( 'returns false on object deep-but-referentially-unequal values', () => { + const a = { foo: {} }; + const b = { foo: {} }; + + expect( isShallowEqual( a, b ) ).toBe( false ); + expect( isShallowEqual( b, a ) ).toBe( false ); + } ); + it( 'returns false if a array has more keys than b', () => { const a = [ 1, 2 ]; const b = [ 1 ]; From b944ed777b54c8fd93cc894040218550e0fd3c7f Mon Sep 17 00:00:00 2001 From: Marcus Kazmierczak Date: Mon, 28 Jan 2019 11:55:26 -0800 Subject: [PATCH 03/36] Expand block template examples (#13494) * Expand block template examples Adds examples for PHP and JS for using templates. Fixes #8251 * Consolidate examples and cleanup Update function names to be consistent across example. --- .../developers/block-api/block-templates.md | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/docs/designers-developers/developers/block-api/block-templates.md b/docs/designers-developers/developers/block-api/block-templates.md index bbc6efe968ebb4..caa96ff20b5bd4 100644 --- a/docs/designers-developers/developers/block-api/block-templates.md +++ b/docs/designers-developers/developers/block-api/block-templates.md @@ -17,18 +17,50 @@ Planned additions: Templates can be declared in JS or in PHP as an array of blockTypes (block name and optional attributes). +The first example in PHP creates a template for posts that includes an image block to start, you can add as many or as few blocks to your template as needed. + +PHP example: + +```php +template = array( + array( 'core/image' ), + ); +} +add_action( 'init', 'myplugin_register_template' ); +``` + +The following example in JavaScript creates a new block using [InnerBlocks](/packages/editor/src/components/inner-blocks) and templates, when inserted creates a set of blocks based off the template. + ```js -const template = [ - [ 'block/name', {} ], // [ blockName, attributes ] +const el = wp.element.createElement; +const { registerBlockType } = wp.blocks; +const { InnerBlocks } = wp.editor; + +const BLOCKS_TEMPLATE = [ + [ 'core/image', {} ], + [ 'core/paragraph', { placeholder: 'Image Details' } ], ]; -``` -```php -'template' => array( - array( 'block/name' ), -), +registerBlockType( 'myplugin/template', { + title: 'My Template Block', + category: 'widgets', + edit: ( props ) => { + return el( InnerBlocks, { + template: BLOCKS_TEMPLATE, + templateLock: false + }); + }, + save: ( props ) => { + return el( InnerBlocks.Content, {} ); + }, +}); ``` +See the [Meta Block Tutorial](/docs/designers-developers/developers/tutorials/metabox/meta-block-5-finishing.md) for a full example of a template in use. + ## Custom Post types A custom post type can register its own template during registration: @@ -61,20 +93,7 @@ add_action( 'init', 'myplugin_register_book_post_type' ); Sometimes the intention might be to lock the template on the UI so that the blocks presented cannot be manipulated. This is achieved with a `template_lock` property. ```php -'template_lock' => 'all', // or 'insert' to allow moving -``` - -*Options:* - -- `all` — prevents all operations. It is not possible to insert new blocks, move existing blocks, or delete blocks. -- `insert` — prevents inserting or removing blocks, but allows moving existing blocks. - -## Existing Post Types - -It is also possible to assign a template to an existing post type like "posts" and "pages": - -```php -function my_add_template_to_posts() { +function myplugin_register_template() { $post_type_object = get_post_type_object( 'post' ); $post_type_object->template = array( array( 'core/paragraph', array( @@ -83,9 +102,14 @@ function my_add_template_to_posts() { ); $post_type_object->template_lock = 'all'; } -add_action( 'init', 'my_add_template_to_posts' ); +add_action( 'init', 'myplugin_register_template' ); ``` +*Options:* + +- `all` — prevents all operations. It is not possible to insert new blocks, move existing blocks, or delete blocks. +- `insert` — prevents inserting or removing blocks, but allows moving existing blocks. + ## Nested Templates Container blocks like the columns blocks also support templates. This is achieved by assigning a nested template to the block. From 77f617603bb369cd16e8ed76ef636b2b82afaaac Mon Sep 17 00:00:00 2001 From: Jefferson Rabb Date: Tue, 29 Jan 2019 02:32:36 -0500 Subject: [PATCH 04/36] Focal Point Picker for the Cover Block (#10925) --- docs/manifest.json | 6 + packages/block-library/src/cover/index.js | 21 ++ .../src/focal-point-picker/README.md | 68 +++++ .../src/focal-point-picker/index.js | 251 ++++++++++++++++++ .../src/focal-point-picker/style.scss | 75 ++++++ packages/components/src/index.js | 1 + packages/components/src/style.scss | 1 + 7 files changed, 423 insertions(+) create mode 100644 packages/components/src/focal-point-picker/README.md create mode 100644 packages/components/src/focal-point-picker/index.js create mode 100644 packages/components/src/focal-point-picker/style.scss diff --git a/docs/manifest.json b/docs/manifest.json index e7d2a88e11cc59..e82a59211c3923 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -899,6 +899,12 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/components/src/external-link/README.md", "parent": "components" }, + { + "title": "FocalPointPicker", + "slug": "focal-point-picker", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/components/src/focal-point-picker/README.md", + "parent": "components" + }, { "title": "FocusableIframe", "slug": "focusable-iframe", diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js index b1ceb293c336f4..1df4323902ecb1 100644 --- a/packages/block-library/src/cover/index.js +++ b/packages/block-library/src/cover/index.js @@ -7,6 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { + FocalPointPicker, IconButton, PanelBody, RangeControl, @@ -67,6 +68,9 @@ const blockAttributes = { type: 'string', default: 'image', }, + focalPoint: { + type: 'object', + }, }; export const name = 'core/cover'; @@ -175,6 +179,7 @@ export const settings = { backgroundType, contentAlign, dimRatio, + focalPoint, hasParallax, id, title, @@ -224,6 +229,10 @@ export const settings = { backgroundColor: overlayColor.color, }; + if ( focalPoint ) { + style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`; + } + const controls = ( @@ -265,6 +274,14 @@ export const settings = { onChange={ toggleParallax } /> ) } + { IMAGE_BACKGROUND_TYPE === backgroundType && ! hasParallax && ( + setAttributes( { focalPoint: value } ) } + /> + ) } { + const url = '/path/to/image'; + const dimensions = { + width: 400, + height: 100 + }; + return ( + setState( { focalPoint } ) } + /> + ) +} ); + +/* Example function to render the CSS styles based on Focal Point Picker value */ +const renderImageContainerWithFocalPoint = ( url, focalPoint ) => { + const style = { + backgroundImage: `url(${ url })` , + backgroundPosition: `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%` + } + return
; +}; +``` + +## Props + +### `url` + +- Type: `Text` +- Required: Yes +- Description: URL of the image to be displayed + +### `dimensions` + +- Type: `Object` +- Required: Yes +- Description: An object describing the height and width of the image. Requires two paramaters: `height`, `width`. + +### `value` + +- Type: `Object` +- Required: Yes +- Description: The focal point. Should be an object containing `x` and `y` params. + +### `onChange` + +- Type: `Function` +- Required: Yes +- Description: Callback which is called when the focal point changes. diff --git a/packages/components/src/focal-point-picker/index.js b/packages/components/src/focal-point-picker/index.js new file mode 100644 index 00000000000000..315c28f7303b7b --- /dev/null +++ b/packages/components/src/focal-point-picker/index.js @@ -0,0 +1,251 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Component, createRef } from '@wordpress/element'; +import { withInstanceId, compose } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import BaseControl from '../base-control'; +import withFocusOutside from '../higher-order/with-focus-outside'; +import { Path, SVG } from '../primitives'; + +const TEXTCONTROL_MIN = 0; +const TEXTCONTROL_MAX = 100; + +export class FocalPointPicker extends Component { + constructor() { + super( ...arguments ); + this.onMouseMove = this.onMouseMove.bind( this ); + this.state = { + isDragging: false, + bounds: {}, + percentages: {}, + }; + this.containerRef = createRef(); + this.imageRef = createRef(); + this.horizontalPositionChanged = this.horizontalPositionChanged.bind( this ); + this.verticalPositionChanged = this.verticalPositionChanged.bind( this ); + this.onLoad = this.onLoad.bind( this ); + } + componentDidMount() { + this.setState( { + percentages: this.props.value, + } ); + } + componentDidUpdate( prevProps ) { + if ( prevProps.url !== this.props.url ) { + this.setState( { + isDragging: false, + } ); + } + } + calculateBounds() { + const bounds = { + top: 0, + left: 0, + bottom: 0, + right: 0, + width: 0, + height: 0, + }; + if ( ! this.imageRef.current ) { + return bounds; + } + const dimensions = { + width: this.imageRef.current.clientWidth, + height: this.imageRef.current.clientHeight, + }; + const pickerDimensions = this.pickerDimensions(); + const widthRatio = pickerDimensions.width / dimensions.width; + const heightRatio = pickerDimensions.height / dimensions.height; + if ( heightRatio >= widthRatio ) { + bounds.width = bounds.right = pickerDimensions.width; + bounds.height = dimensions.height * widthRatio; + bounds.top = ( pickerDimensions.height - bounds.height ) / 2; + bounds.bottom = bounds.top + bounds.height; + } else { + bounds.height = bounds.bottom = pickerDimensions.height; + bounds.width = dimensions.width * heightRatio; + bounds.left = ( pickerDimensions.width - bounds.width ) / 2; + bounds.right = bounds.left + bounds.width; + } + return bounds; + } + onLoad() { + this.setState( { + bounds: this.calculateBounds(), + } ); + } + onMouseMove( event ) { + const { isDragging, bounds } = this.state; + const { onChange } = this.props; + + if ( isDragging ) { + const pickerDimensions = this.pickerDimensions(); + const cursorPosition = { + left: event.pageX - pickerDimensions.left, + top: event.pageY - pickerDimensions.top, + }; + const left = Math.max( + bounds.left, + Math.min( + cursorPosition.left, bounds.right + ) + ); + const top = Math.max( + bounds.top, + Math.min( + cursorPosition.top, bounds.bottom + ) + ); + const percentages = { + x: ( left - bounds.left ) / ( pickerDimensions.width - ( bounds.left * 2 ) ), + y: ( top - bounds.top ) / ( pickerDimensions.height - ( bounds.top * 2 ) ), + }; + this.setState( { percentages }, function() { + onChange( { + x: this.state.percentages.x, + y: this.state.percentages.y, + } ); + } ); + } + } + fractionToPercentage( fraction ) { + return Math.round( fraction * 100 ); + } + horizontalPositionChanged( event ) { + this.positionChangeFromTextControl( 'x', event.target.value ); + } + verticalPositionChanged( event ) { + this.positionChangeFromTextControl( 'y', event.target.value ); + } + positionChangeFromTextControl( axis, value ) { + const { onChange } = this.props; + const { percentages } = this.state; + const cleanValue = Math.max( Math.min( parseInt( value ), 100 ), 0 ); + percentages[ axis ] = cleanValue ? cleanValue / 100 : 0; + this.setState( { percentages }, function() { + onChange( { + x: this.state.percentages.x, + y: this.state.percentages.y, + } ); + } ); + } + pickerDimensions() { + if ( this.containerRef.current ) { + return { + width: this.containerRef.current.clientWidth, + height: this.containerRef.current.clientHeight, + top: this.containerRef.current.getBoundingClientRect().top + document.body.scrollTop, + left: this.containerRef.current.getBoundingClientRect().left, + }; + } + return { + width: 0, + height: 0, + left: 0, + top: 0, + }; + } + handleFocusOutside() { + this.setState( { + isDragging: false, + } ); + } + render() { + const { instanceId, url, value, label, help, className } = this.props; + const { bounds, isDragging, percentages } = this.state; + const pickerDimensions = this.pickerDimensions(); + const iconCoordinates = { + left: ( value.x * ( pickerDimensions.width - ( bounds.left * 2 ) ) ) + bounds.left, + top: ( value.y * ( pickerDimensions.height - ( bounds.top * 2 ) ) ) + bounds.top, + }; + const iconContainerStyle = { + left: `${ iconCoordinates.left }px`, + top: `${ iconCoordinates.top }px`, + }; + const iconContainerClasses = classnames( + 'components-focal-point-picker__icon_container', + isDragging ? 'is-dragging' : null + ); + const id = `inspector-focal-point-picker-control-${ instanceId }`; + const horizontalPositionId = `inspector-focal-point-picker-control-horizontal-position-${ instanceId }`; + const verticalPositionId = `inspector-focal-point-picker-control-horizontal-position-${ instanceId }`; + return ( + +
+
this.setState( { isDragging: true } ) } + onDragStart={ () => this.setState( { isDragging: true } ) } + onMouseUp={ () => this.setState( { isDragging: false } ) } + onDrop={ () => this.setState( { isDragging: false } ) } + onMouseMove={ this.onMouseMove } + ref={ this.containerRef } + role="button" + tabIndex="-1" + > + Dimensions helper +
+ + + + +
+
+
+
+ + + % + + + + % + +
+
+ ); + } +} + +FocalPointPicker.defaultProps = { + url: null, + value: { + x: 0.5, + y: 0.5, + }, + onChange: () => {}, +}; + +export default compose( [ withInstanceId, withFocusOutside ] )( FocalPointPicker ); diff --git a/packages/components/src/focal-point-picker/style.scss b/packages/components/src/focal-point-picker/style.scss new file mode 100644 index 00000000000000..f93f2e57ad6886 --- /dev/null +++ b/packages/components/src/focal-point-picker/style.scss @@ -0,0 +1,75 @@ +.components-focal-point-picker-wrapper { + background-color: transparent; + border: 1px solid $light-gray-500; + height: 200px; + width: 100%; + padding: 14px; +} + +.components-focal-point-picker { + align-items: center; + cursor: pointer; + display: flex; + height: 100%; + justify-content: center; + position: relative; + width: 100%; + + img { + height: auto; + max-height: 100%; + max-width: 100%; + width: auto; + user-select: none; + } +} + +.components-focal-point-picker__icon_container { + background-color: transparent; + cursor: grab; + height: 30px; + opacity: 0.8; + position: absolute; + will-change: transform; + width: 30px; + z-index: 10000; + + &.is-dragging { + cursor: grabbing; + } +} + +.components-focal-point-picker__icon { + display: block; + height: 100%; + left: -15px; + position: absolute; + top: -15px; + width: 100%; + + .components-focal-point-picker__icon-outline { + fill: $white; + } + + .components-focal-point-picker__icon-fill { + fill: theme(primary); + } +} + +.components-focal-point-picker_position-display-container { + margin: 1em 0; + display: flex; + + .components-base-control__field { + margin: 0 1em 0 0; + } + + input[type="number"].components-text-control__input { // Needs specificity to override padding. + max-width: 4em; + padding: 6px 4px; + } + + span { + margin: 0 0 0 0.2em; + } +} diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 415cd5a1be5dc4..f892867c7a4005 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -19,6 +19,7 @@ export { default as DropZoneProvider } from './drop-zone/provider'; export { default as Dropdown } from './dropdown'; export { default as DropdownMenu } from './dropdown-menu'; export { default as ExternalLink } from './external-link'; +export { default as FocalPointPicker } from './focal-point-picker'; export { default as FocusableIframe } from './focusable-iframe'; export { default as FontSizePicker } from './font-size-picker'; export { default as FormFileUpload } from './form-file-upload'; diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index c3e8b38e7a7efd..046c7b6264efdc 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -13,6 +13,7 @@ @import "./drop-zone/style.scss"; @import "./dropdown-menu/style.scss"; @import "./external-link/style.scss"; +@import "./focal-point-picker/style.scss"; @import "./font-size-picker/style.scss"; @import "./form-file-upload/style.scss"; @import "./form-toggle/style.scss"; From 4a1fc6fa6b405b6c262500335cc1daffaba60173 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 29 Jan 2019 07:36:28 +0000 Subject: [PATCH 05/36] Block switch: ensure hover is reset when style changed (#12317) When a block style is selected we should reset the hover state so that any component using this will be adjusted accordingly --- packages/editor/src/components/block-styles/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/editor/src/components/block-styles/index.js b/packages/editor/src/components/block-styles/index.js index 6892df08c6d06a..ad108daa18643f 100644 --- a/packages/editor/src/components/block-styles/index.js +++ b/packages/editor/src/components/block-styles/index.js @@ -79,6 +79,7 @@ function BlockStyles( { function updateClassName( style ) { const updatedClassName = replaceActiveStyle( className, activeStyle, style ); onChangeClassName( updatedClassName ); + onHoverClassName( null ); onSwitch(); } From cd0af05939345641fb0b99335f87321797d4e86d Mon Sep 17 00:00:00 2001 From: Hendrik Luehrsen Date: Tue, 29 Jan 2019 08:37:27 +0100 Subject: [PATCH 06/36] Add innerBlocks as an argument to the transform function (#11979) Co-Authored-By: Hendrik Luehrsen --- .../block-api/block-registration.md | 33 ++++++ packages/blocks/CHANGELOG.md | 6 ++ packages/blocks/src/api/factory.js | 7 +- packages/blocks/src/api/test/factory.js | 100 ++++++++++++++++++ .../plugins/inner-blocks-templates/index.js | 71 ++++++++++--- 5 files changed, 202 insertions(+), 15 deletions(-) diff --git a/docs/designers-developers/developers/block-api/block-registration.md b/docs/designers-developers/developers/block-api/block-registration.md index 90d0752ff170ea..e36628c7249e04 100644 --- a/docs/designers-developers/developers/block-api/block-registration.md +++ b/docs/designers-developers/developers/block-api/block-registration.md @@ -311,6 +311,39 @@ transforms: { ``` {% end %} +A block with innerBlocks can also be transformed from and to another block with innerBlocks. + +{% codetabs %} +{% ES5 %} +```js +transforms: { + to: [ + { + type: 'block', + blocks: [ 'some/block-with-innerblocks' ], + transform: function( attributes, innerBlocks ) { + return createBlock( 'some/other-block-with-innerblocks', attributes, innerBlocks ); + }, + }, + ], +}, +``` +{% ESNext %} +```js +transforms: { + to: [ + { + type: 'block', + blocks: [ 'some/block-with-innerblocks' ], + transform: ( attributes, innerBlocks ) => { + return createBlock( 'some/other-block-with-innerblocks', attributes, innerBlocks); + }, + }, + ], +}, +``` +{% end %} + An optional `isMatch` function can be specified on a transform object. This provides an opportunity to perform additional checks on whether a transform should be possible. Returning `false` from this function will prevent the transform from being displayed as an option to the user. {% codetabs %} diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index 1f40a53235f4cd..4d607751d8a31b 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.1.0 (Unreleased) + +### New Feature + +- Blocks' `transforms` will receive `innerBlocks` as the second argument (or an array of each block's respective `innerBlocks` for a multi-transform). + ## 6.0.5 (2019-01-03) ## 6.0.4 (2018-12-12) diff --git a/packages/blocks/src/api/factory.js b/packages/blocks/src/api/factory.js index 9068a8b3d8cd3b..c7bd01f2c699c4 100644 --- a/packages/blocks/src/api/factory.js +++ b/packages/blocks/src/api/factory.js @@ -346,9 +346,12 @@ export function switchToBlockType( blocks, name ) { let transformationResults; if ( transformation.isMultiBlock ) { - transformationResults = transformation.transform( blocksArray.map( ( currentBlock ) => currentBlock.attributes ) ); + transformationResults = transformation.transform( + blocksArray.map( ( currentBlock ) => currentBlock.attributes ), + blocksArray.map( ( currentBlock ) => currentBlock.innerBlocks ) + ); } else { - transformationResults = transformation.transform( firstBlock.attributes ); + transformationResults = transformation.transform( firstBlock.attributes, firstBlock.innerBlocks ); } // Ensure that the transformation function returned an object or an array diff --git a/packages/blocks/src/api/test/factory.js b/packages/blocks/src/api/test/factory.js index eaf222b8b5c866..11249b08d36ced 100644 --- a/packages/blocks/src/api/test/factory.js +++ b/packages/blocks/src/api/test/factory.js @@ -1122,6 +1122,106 @@ describe( 'block factory', () => { value: 'smoked ribs', } ); } ); + + it( 'should pass through inner blocks to transform', () => { + registerBlockType( 'core/updated-columns-block', { + attributes: { + value: { + type: 'string', + }, + }, + transforms: { + from: [ { + type: 'block', + blocks: [ 'core/columns-block' ], + transform( attributes, innerBlocks ) { + return createBlock( + 'core/updated-columns-block', + attributes, + innerBlocks.map( ( innerBlock ) => { + return cloneBlock( innerBlock, { + value: 'after', + } ); + } ), + ); + }, + } ], + }, + save: noop, + category: 'common', + title: 'updated columns block', + } ); + registerBlockType( 'core/columns-block', defaultBlockSettings ); + registerBlockType( 'core/column-block', defaultBlockSettings ); + + const block = createBlock( + 'core/columns-block', + {}, + [ createBlock( 'core/column-block', { value: 'before' } ) ] + ); + + const transformedBlocks = switchToBlockType( block, 'core/updated-columns-block' ); + + expect( transformedBlocks ).toHaveLength( 1 ); + expect( transformedBlocks[ 0 ].innerBlocks ).toHaveLength( 1 ); + expect( transformedBlocks[ 0 ].innerBlocks[ 0 ].attributes.value ).toBe( 'after' ); + } ); + + it( 'should pass through inner blocks to transform (multi)', () => { + registerBlockType( 'core/updated-columns-block', { + attributes: { + value: { + type: 'string', + }, + }, + transforms: { + from: [ { + type: 'block', + blocks: [ 'core/columns-block' ], + isMultiBlock: true, + transform( blocksAttributes, blocksInnerBlocks ) { + return blocksAttributes.map( ( attributes, i ) => { + return createBlock( + 'core/updated-columns-block', + attributes, + blocksInnerBlocks[ i ].map( ( innerBlock ) => { + return cloneBlock( innerBlock, { + value: 'after' + i, + } ); + } ), + ); + } ); + }, + } ], + }, + save: noop, + category: 'common', + title: 'updated columns block', + } ); + registerBlockType( 'core/columns-block', defaultBlockSettings ); + registerBlockType( 'core/column-block', defaultBlockSettings ); + + const blocks = [ + createBlock( + 'core/columns-block', + {}, + [ createBlock( 'core/column-block', { value: 'before' } ) ] + ), + createBlock( + 'core/columns-block', + {}, + [ createBlock( 'core/column-block', { value: 'before' } ) ] + ), + ]; + + const transformedBlocks = switchToBlockType( blocks, 'core/updated-columns-block' ); + + expect( transformedBlocks ).toHaveLength( 2 ); + expect( transformedBlocks[ 0 ].innerBlocks ).toHaveLength( 1 ); + expect( transformedBlocks[ 0 ].innerBlocks[ 0 ].attributes.value ).toBe( 'after0' ); + expect( transformedBlocks[ 1 ].innerBlocks ).toHaveLength( 1 ); + expect( transformedBlocks[ 1 ].innerBlocks[ 0 ].attributes.value ).toBe( 'after1' ); + } ); } ); describe( 'getBlockTransforms', () => { diff --git a/packages/e2e-tests/plugins/inner-blocks-templates/index.js b/packages/e2e-tests/plugins/inner-blocks-templates/index.js index 0922fe106cecf2..6c0b62c51fea4e 100644 --- a/packages/e2e-tests/plugins/inner-blocks-templates/index.js +++ b/packages/e2e-tests/plugins/inner-blocks-templates/index.js @@ -1,5 +1,6 @@ ( function() { var registerBlockType = wp.blocks.registerBlockType; + var createBlock = wp.blocks.createBlock; var el = wp.element.createElement; var InnerBlocks = wp.editor.InnerBlocks; var __ = wp.i18n.__; @@ -28,10 +29,10 @@ edit: function( props ) { return el( - InnerBlocks, - { - template: TEMPLATE, - } + InnerBlocks, + { + template: TEMPLATE, + } ); }, @@ -45,11 +46,11 @@ edit: function( props ) { return el( - InnerBlocks, - { - template: TEMPLATE, - templateLock: 'all', - } + InnerBlocks, + { + template: TEMPLATE, + templateLock: 'all', + } ); }, @@ -63,13 +64,57 @@ edit: function( props ) { return el( - InnerBlocks, - { - template: TEMPLATE_PARAGRAPH_PLACEHOLDER, - } + InnerBlocks, + { + template: TEMPLATE_PARAGRAPH_PLACEHOLDER, + } ); }, save, } ); + + registerBlockType( 'test/test-inner-blocks-transformer-target', { + title: 'Test Inner Blocks transformer target', + icon: 'cart', + category: 'common', + + transforms: { + from: [ + { + type: 'block', + blocks: [ + 'test/i-dont-exist', + 'test/test-inner-blocks-no-locking', + 'test/test-inner-blocks-locking-all', + 'test/test-inner-blocks-paragraph-placeholder' + ], + transform: function( attributes, innerBlocks ) { + return createBlock( 'test/test-inner-blocks-transformer-target', attributes, innerBlocks ); + }, + }, + ], + to: [ + { + type: 'block', + blocks: [ 'test/i-dont-exist' ], + transform: function( attributes, innerBlocks ) { + return createBlock( 'test/test-inner-blocks-transformer-target', attributes, innerBlocks ); + }, + } + ] + }, + + edit: function( props ) { + return el( + InnerBlocks, + { + template: TEMPLATE, + } + ); + }, + + save, + } ); + } )(); From d1c78bf149bd4bca3af968e3d3b7162cd670359a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 29 Jan 2019 02:38:42 -0500 Subject: [PATCH 07/36] Add default block style if missing (#12519) * Add default block style if missing * Add 'block style' context --- .../editor/src/components/block-styles/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/editor/src/components/block-styles/index.js b/packages/editor/src/components/block-styles/index.js index ad108daa18643f..c44f8f686e191d 100644 --- a/packages/editor/src/components/block-styles/index.js +++ b/packages/editor/src/components/block-styles/index.js @@ -11,6 +11,8 @@ import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; import TokenList from '@wordpress/token-list'; import { ENTER, SPACE } from '@wordpress/keycodes'; +import { _x } from '@wordpress/i18n'; +import { getBlockType } from '@wordpress/blocks'; /** * Internal dependencies @@ -68,6 +70,7 @@ function BlockStyles( { onChangeClassName, name, attributes, + type, onSwitch = noop, onHoverClassName = noop, } ) { @@ -75,6 +78,17 @@ function BlockStyles( { return null; } + if ( ! type.styles && ! find( styles, 'isDefault' ) ) { + styles = [ + { + name: 'default', + label: _x( 'Default', 'block style' ), + isDefault: true, + }, + ...styles, + ]; + } + const activeStyle = getActiveStyle( styles, className ); function updateClassName( style ) { const updatedClassName = replaceActiveStyle( className, activeStyle, style ); @@ -132,12 +146,14 @@ export default compose( [ const { getBlock } = select( 'core/editor' ); const { getBlockStyles } = select( 'core/blocks' ); const block = getBlock( clientId ); + const blockType = getBlockType( block.name ); return { name: block.name, attributes: block.attributes, className: block.attributes.className || '', styles: getBlockStyles( block.name ), + type: blockType, }; } ), withDispatch( ( dispatch, { clientId } ) => { From bf124b7f10b864694e6c1ffecb0f9fac161472b7 Mon Sep 17 00:00:00 2001 From: Sheri Bigelow Date: Tue, 29 Jan 2019 00:41:50 -0700 Subject: [PATCH 08/36] Update Question template for new issues (#13351) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Question template for new issues Now that Gutenberg has shipped in WordPress 5.0, we should consider updating the Question template for new issues to gently help people move their Gutenberg help requests to the main support forum as a first stop. This is because the ideal workflow is to ask for help and complete any troubleshooting needed with support first, which can then be moved to GitHub as a bug report once sufficient details are uncovered. This will help reduce duplicate requests, increase the quality of bug reports, and help developers working on the project focus on feature work and bug fixes for verified bug reports. * Update Question template to add StackExchange Add https://wordpress.stackexchange.com/ as a fallback suggested resource for where to get help with technical help requests. Note: this was suggested in a Core Editor Weekly Chat on 2019-01-23 at https://wordpress.slack.com/archives/C02QB2JS7/p1548252431142200. * Update Question template to ask to search first I met with the WordPress Core Support Team to discuss changes to the Question template and we decided to: * Add a line to ask people to search before posting! * Move the handbook note up. * Update the title to "Help Request". * Update the description to "Please post help requests or ‘how to’ questions in support channels first". Link to the discussion for reference: https://wordpress.slack.com/archives/C02RQC6RW/p1548349915905600 * Update Question template to move the handbook link to the end Move the handbook note to the bottom, to be friendlier. * Update Question template with editorial feedback Mending the tone after an editorial review! 😍 --- .github/ISSUE_TEMPLATE/Custom.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index 89783e5bbea906..196cdeb63305fa 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -1,13 +1,17 @@ --- -name: Question -about: Questions or 'how to' about Gutenberg +name: Help Request +about: Please post help requests or ‘how to’ questions in support channels first --- -If you have a question you have a few places that you can ask this: +Search first! Your issue may have already been reported. -- Support Forums: https://wordpress.org/support/plugin/gutenberg -- Handbook: https://wordpress.org/gutenberg/handbook -- https://chat.wordpress.org #core-editor +For general help requests, please post in the support forum at https://wordpress.org/support/forum/how-to-and-troubleshooting/. -If you are unable to ask in those places you can ask here, however you will get faster responses through those recommended places. +Technical help requests have their own section of the support forum at https://wordpress.org/support/forum/wp-advanced/. + +You may also ask for technical support at https://wordpress.stackexchange.com/. + +Please make sure you have checked the Handbook at https://wordpress.org/gutenberg/handbook before asking your question. + +Thank you! From 9e523ceb3221d6eb89a9aa69e3f5db956a483f9d Mon Sep 17 00:00:00 2001 From: Ajit Bohra Date: Tue, 29 Jan 2019 14:32:16 +0530 Subject: [PATCH 09/36] Docs: update links (#13531) * Docs: update links * Update link Co-Authored-By: ajitbohra * Update link Co-Authored-By: ajitbohra * Update link Co-Authored-By: ajitbohra * Update link Co-Authored-By: ajitbohra * Update link Co-Authored-By: ajitbohra * Update & fix dead links * Update README.md * Update writing-your-first-block-type.md * Update scripts.md --- docs/contributors/reference.md | 10 +-- docs/contributors/scripts.md | 64 +++++++++---------- .../backward-compatibility/deprecations.md | 6 +- .../developers/block-api/README.md | 4 +- .../developers/block-api/block-attributes.md | 2 +- .../developers/block-api/block-deprecation.md | 8 +-- .../developers/block-api/block-edit-save.md | 8 +-- .../block-api/block-registration.md | 12 ++-- .../developers/data/README.md | 16 ++--- .../developers/filters/README.md | 2 +- .../developers/filters/block-filters.md | 2 +- .../developers/themes/theme-support.md | 2 +- .../block-tutorial/creating-dynamic-blocks.md | 2 +- .../generate-blocks-with-wp-cli.md | 16 ++--- ...roducing-attributes-and-editable-fields.md | 2 +- .../writing-your-first-block-type.md | 4 +- .../javascript/extending-the-block-editor.md | 4 +- .../developers/tutorials/javascript/readme.md | 12 ++-- .../tutorials/metabox/meta-block-3-add.md | 2 +- .../sidebar-tutorial/plugin-sidebar-0.md | 14 ++-- .../plugin-sidebar-1-up-and-running.md | 2 +- .../plugin-sidebar-2-styles-and-controls.md | 2 +- .../plugin-sidebar-3-register-meta.md | 2 +- .../plugin-sidebar-4-initialize-input.md | 4 +- .../plugin-sidebar-5-update-meta.md | 2 +- docs/designers-developers/faq.md | 8 +-- docs/designers-developers/key-concepts.md | 2 +- docs/readme.md | 4 +- docs/tool/generator.js | 2 +- packages/components/CONTRIBUTING.md | 2 +- 30 files changed, 111 insertions(+), 111 deletions(-) diff --git a/docs/contributors/reference.md b/docs/contributors/reference.md index 551c6f82ab8e72..95c0b603323e50 100644 --- a/docs/contributors/reference.md +++ b/docs/contributors/reference.md @@ -1,9 +1,9 @@ # Reference -- [Glossary](../../docs/designers-developers/glossary.md) -- [Coding Guidelines](../../docs/contributors/coding-guidelines.md) -- [Testing Overview](../../docs/contributors/testing-overview.md) -- [Frequently Asked Questions](../../docs/designers-developers/faq.md) +- [Glossary](/docs/designers-developers/glossary.md) +- [Coding Guidelines](/docs/contributors/coding-guidelines.md) +- [Testing Overview](/docs/contributors/testing-overview.md) +- [Frequently Asked Questions](/docs/designers-developers/faq.md) ## Logo Gutenberg Logo @@ -14,4 +14,4 @@ Released under GPL license, made by [Cristel Rossignol](https://twitter.com/cris ## Mockups -Mockup Sketch files are available in the Design section. +Mockup Sketch files are available in [the Design section](/docs/designers-developers/designers/design-resources.md). diff --git a/docs/contributors/scripts.md b/docs/contributors/scripts.md index dd7e1d295d528c..5d5c1f672a4b2a 100644 --- a/docs/contributors/scripts.md +++ b/docs/contributors/scripts.md @@ -8,38 +8,38 @@ The editor includes a number of packages to enable various pieces of functionali | Script Name | Handle | Description | |-------------|--------|-------------| -| [Blob](https://wordpress.org/gutenberg/handbook/packages/packages-blob/) | wp-blob | Blob utilities | -| [Block Library](https://wordpress.org/gutenberg/handbook/packages/packages-block-library/) | wp-block-library | Block library for the editor | -| [Blocks](https://wordpress.org/gutenberg/handbook/packages/packages-blocks/) | wp-blocks | Block creations | -| [Block Serialization Default Parser](https://wordpress.org/gutenberg/handbook/packages/packages-block-serialization-default-parser/) | wp-block-serialization-default-parser | Default block serialization parser implementations for WordPress documents | -| [Block Serialization Spec Parser](https://wordpress.org/gutenberg/handbook/packages/packages-block-serialization-spec-parser/) | wp-block-serialization-spec-parser | Grammar file (grammar.pegjs) for WordPress posts | -| [Components](https://wordpress.org/gutenberg/handbook/packages/packages-components/) | wp-components | Generic components to be used for creating common UI elements | -| [Compose](https://wordpress.org/gutenberg/handbook/packages/packages-compose/) | wp-compose | Collection of handy Higher Order Components (HOCs) | -| [Core Data](https://wordpress.org/gutenberg/handbook/packages/packages-core-data/) | wp-core-data | Simplify access to and manipulation of core WordPress entities | -| [Data](https://wordpress.org/gutenberg/handbook/packages/packages-data/) | wp-data | Data module serves as a hub to manage application state for both plugins and WordPress itself | -| [Date](https://wordpress.org/gutenberg/handbook/packages/packages-date/) | wp-date | Date module for WordPress | -| [Deprecated](https://wordpress.org/gutenberg/handbook/packages/packages-deprecated/) | wp-deprecated | Utility to log a message to notify developers about a deprecated feature | -| [Dom](https://wordpress.org/gutenberg/handbook/packages/packages-dom/) | wp-dom | DOM utilities module for WordPress | -| [Dom Ready](https://wordpress.org/gutenberg/handbook/packages/packages-dom-ready/) | wp-dom-ready | Execute callback after the DOM is loaded | -| [Editor](https://wordpress.org/gutenberg/handbook/packages/packages-editor/) | wp-editor | Building blocks for WordPress editors | -| [Edit Post](https://wordpress.org/gutenberg/handbook/packages/packages-edit-post/) | wp-edit-post | Edit Post Module for WordPress | -| [Element](https://wordpress.org/gutenberg/handbook/packages/packages-element/) | wp-element |Element is, quite simply, an abstraction layer atop [React](https://reactjs.org/) | -| [Escape Html](https://wordpress.org/gutenberg/handbook/packages/packages-escape-html/) | wp-escape-html | Escape HTML utils | -| [Hooks](https://wordpress.org/gutenberg/handbook/packages/packages-hooks/) | wp-hooks | A lightweight and efficient EventManager for JavaScript | -| [Html Entities](https://wordpress.org/gutenberg/handbook/packages/packages-html-entities/) | wp-html-entities | HTML entity utilities for WordPress | -| [I18N](https://wordpress.org/gutenberg/handbook/packages/packages-i18n/) | wp-i18n | Internationalization utilities for client-side localization | -| [Is Shallow Equal](https://wordpress.org/gutenberg/handbook/packages/packages-is-shallow-equal/) | wp-is-shallow-equal | A function for performing a shallow comparison between two objects or arrays | -| [Keycodes](https://wordpress.org/gutenberg/handbook/packages/packages-keycodes/) | wp-keycodes | Keycodes utilities for WordPress, used to check the key pressed in events like `onKeyDown` | -| [List Reusable Bocks](https://wordpress.org/gutenberg/handbook/packages/packages-list-reusable-blocks/) | wp-list-reusable-blocks | Package used to add import/export links to the listing page of the reusable blocks | -| [NUX](https://wordpress.org/gutenberg/handbook/packages/packages-nux/) | wp-nux | Components, and wp.data methods useful for onboarding a new user to the WordPress admin interface | -| [Plugins](https://wordpress.org/gutenberg/handbook/packages/packages-plugins/) | wp-plugins | Plugins module for WordPress | -| [Redux Routine](https://wordpress.org/gutenberg/handbook/packages/packages-redux-routine/) | wp-redux-routine | Redux middleware for generator coroutines | -| [Rich Text](https://wordpress.org/gutenberg/handbook/packages/packages-rich-text/) | wp-rich-text | Helper functions to convert HTML or a DOM tree into a rich text value and back | -| [Shortcode](https://wordpress.org/gutenberg/handbook/packages/packages-shortcode/) | wp-shortcode | Shortcode module for WordPress | -| [Token List](https://wordpress.org/gutenberg/handbook/packages/packages-token-list/) | wp-token-list | Constructable, plain JavaScript [DOMTokenList](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList) implementation, supporting non-browser runtimes | -| [URL](https://wordpress.org/gutenberg/handbook/packages/packages-url/) | wp-url | A collection of utilities to manipulate URLs | -| [Viewport](https://wordpress.org/gutenberg/handbook/packages/packages-viewport/) | wp-viewport | Module for responding to changes in the browser viewport size | -| [Wordcount](https://wordpress.org/gutenberg/handbook/packages/packages-wordcount/) | wp-wordcount | WordPress word count utility | +| [Blob](/packages/blob/README.md) | wp-blob | Blob utilities | +| [Block Library](/packages/block-library/README.md) | wp-block-library | Block library for the editor | +| [Blocks](/packages/blocks/README.md) | wp-blocks | Block creations | +| [Block Serialization Default Parser](/packages/block-serialization-default-parser/README.md) | wp-block-serialization-default-parser | Default block serialization parser implementations for WordPress documents | +| [Block Serialization Spec Parser](/packages/block-serialization-spec-parser/README.md) | wp-block-serialization-spec-parser | Grammar file (grammar.pegjs) for WordPress posts | +| [Components](/packages/components/README.md) | wp-components | Generic components to be used for creating common UI elements | +| [Compose](/packages/compose/README.md) | wp-compose | Collection of handy Higher Order Components (HOCs) | +| [Core Data](/packages/core-data/README.md) | wp-core-data | Simplify access to and manipulation of core WordPress entities | +| [Data](/packages/data/README.md) | wp-data | Data module serves as a hub to manage application state for both plugins and WordPress itself | +| [Date](/packages/date/README.md) | wp-date | Date module for WordPress | +| [Deprecated](/packages/deprecated/README.md) | wp-deprecated | Utility to log a message to notify developers about a deprecated feature | +| [Dom](/packages/dom/README.md) | wp-dom | DOM utilities module for WordPress | +| [Dom Ready](/packages/dom-ready/README.md) | wp-dom-ready | Execute callback after the DOM is loaded | +| [Editor](/packages/editor/README.md) | wp-editor | Building blocks for WordPress editors | +| [Edit Post](/packages/edit-post/README.md) | wp-edit-post | Edit Post Module for WordPress | +| [Element](/packages/element/README.md) | wp-element |Element is, quite simply, an abstraction layer atop [React](https://reactjs.org/) | +| [Escape Html](/packages/escape-html/README.md) | wp-escape-html | Escape HTML utils | +| [Hooks](/packages/hooks/README.md) | wp-hooks | A lightweight and efficient EventManager for JavaScript | +| [Html Entities](/packages/html-entities/README.md) | wp-html-entities | HTML entity utilities for WordPress | +| [I18N](/packages/i18n/README.md) | wp-i18n | Internationalization utilities for client-side localization | +| [Is Shallow Equal](/packages/is-shallow-equal/README.md) | wp-is-shallow-equal | A function for performing a shallow comparison between two objects or arrays | +| [Keycodes](/packages/keycodes/README.md) | wp-keycodes | Keycodes utilities for WordPress, used to check the key pressed in events like `onKeyDown` | +| [List Reusable Bocks](/packages/list-reusable-blocks/README.md) | wp-list-reusable-blocks | Package used to add import/export links to the listing page of the reusable blocks | +| [NUX](/packages/nux/README.md) | wp-nux | Components, and wp.data methods useful for onboarding a new user to the WordPress admin interface | +| [Plugins](/packages/plugins/README.md) | wp-plugins | Plugins module for WordPress | +| [Redux Routine](/packages/redux-routine/README.md) | wp-redux-routine | Redux middleware for generator coroutines | +| [Rich Text](/packages/rich-text/README.md) | wp-rich-text | Helper functions to convert HTML or a DOM tree into a rich text value and back | +| [Shortcode](/packages/shortcode/README.md) | wp-shortcode | Shortcode module for WordPress | +| [Token List](/packages/token-list/README.md) | wp-token-list | Constructable, plain JavaScript [DOMTokenList](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList) implementation, supporting non-browser runtimes | +| [URL](/packages/url/README.md) | wp-url | A collection of utilities to manipulate URLs | +| [Viewport](/packages/viewport/README.md) | wp-viewport | Module for responding to changes in the browser viewport size | +| [Wordcount](/packages/wordcount/README.md) | wp-wordcount | WordPress word count utility | ## Vendor Scripts diff --git a/docs/designers-developers/developers/backward-compatibility/deprecations.md b/docs/designers-developers/developers/backward-compatibility/deprecations.md index f2ce4c1ee8d2f2..e4a99cff295136 100644 --- a/docs/designers-developers/developers/backward-compatibility/deprecations.md +++ b/docs/designers-developers/developers/backward-compatibility/deprecations.md @@ -214,11 +214,11 @@ The Gutenberg project's deprecation policy is intended to support backward compa ## 3.0.0 - `wp.blocks.registerCoreBlocks` function removed. Please use `wp.coreBlocks.registerCoreBlocks` instead. - - Raw TinyMCE event handlers for `RichText` have been deprecated. Please use [documented props](https://wordpress.org/gutenberg/handbook/block-api/rich-text-api/), ancestor event handler, or onSetup access to the internal editor instance event hub instead. + - Raw TinyMCE event handlers for `RichText` have been deprecated. Please use [documented props](/packages/editor/src/components/rich-text/README.md), ancestor event handler, or onSetup access to the internal editor instance event hub instead. ## 2.8.0 - - `Original autocompleter interface in wp.components.Autocomplete` updated. Please use `latest autocompleter interface` instead. See: https://github.com/WordPress/gutenberg/blob/master/components/autocomplete/README.md. + - `Original autocompleter interface in wp.components.Autocomplete` updated. Please use `latest autocompleter interface` instead. See [autocomplete](/packages/components/src/autocomplete/README.md) for more info. - `getInserterItems`: the `allowedBlockTypes` argument is now mandatory. - `getFrecentInserterItems`: the `allowedBlockTypes` argument is now mandatory. @@ -240,6 +240,6 @@ The Gutenberg project's deprecation policy is intended to support backward compa - `wp.blocks.BlockDescription` component removed. Please use the `description` block property instead. - `wp.blocks.InspectorControls.*` components removed. Please use `wp.components.*` components instead. - - `wp.blocks.source.*` matchers removed. Please use the declarative attributes instead. See: https://wordpress.org/gutenberg/handbook/block-api/attributes/. + - `wp.blocks.source.*` matchers removed. Please use the declarative attributes instead. See [block attributes](/docs/designers-developers/developers/block-api/block-attributes.md) for more info. - `wp.data.select( 'selector', ...args )` removed. Please use `wp.data.select( reducerKey' ).*` instead. - `wp.blocks.MediaUploadButton` component removed. Please use `wp.blocks.MediaUpload` component instead. diff --git a/docs/designers-developers/developers/block-api/README.md b/docs/designers-developers/developers/block-api/README.md index af153d59ba38a4..56d159bda6d135 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 editor. They are the primary way in wh ## 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](../../../../docs/designers-developers/developers/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](../../../../docs/designers-developers/developers/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/block-api/block-attributes.md b/docs/designers-developers/developers/block-api/block-attributes.md index 48a9b36ef83b51..fd61866f5286c4 100644 --- a/docs/designers-developers/developers/block-api/block-attributes.md +++ b/docs/designers-developers/developers/block-api/block-attributes.md @@ -4,7 +4,7 @@ Attribute sources are used to define the strategy by which block attribute values are extracted from saved post content. They provide a mechanism to map from the saved markup to a JavaScript representation of a block. -If no attribute source is specified, the attribute will be saved to (and read from) the block's [comment delimiter](../../../../docs/designers-developers/key-concepts.md#delimiters-and-parsing-expression-grammar). +If no attribute source is specified, the attribute will be saved to (and read from) the block's [comment delimiter](/docs/designers-developers/key-concepts.md#delimiters-and-parsing-expression-grammar). Each source accepts an optional selector as the first argument. If a selector is specified, the source behavior will be run against the corresponding element(s) contained within the block. Otherwise it will be run against the block's root node. diff --git a/docs/designers-developers/developers/block-api/block-deprecation.md b/docs/designers-developers/developers/block-api/block-deprecation.md index 3fd500c1645417..7e64bfb89a9991 100644 --- a/docs/designers-developers/developers/block-api/block-deprecation.md +++ b/docs/designers-developers/developers/block-api/block-deprecation.md @@ -9,9 +9,9 @@ A block can have several deprecated versions. A deprecation will be tried if a p Deprecations are defined on a block type as its `deprecated` property, an array of deprecation objects where each object takes the form: -- `attributes` (Object): The [attributes definition](../../../../docs/designers-developers/developers/block-api/block-attributes.md) of the deprecated form of the block. -- `support` (Object): The [supports definition](../../../../docs/designers-developers/developers/block-api/block-registration.md) of the deprecated form of the block. -- `save` (Function): The [save implementation](../../../../docs/designers-developers/developers/block-api/block-edit-save.md) of the deprecated form of the block. +- `attributes` (Object): The [attributes definition](/docs/designers-developers/developers/block-api/block-attributes.md) of the deprecated form of the block. +- `support` (Object): The [supports definition](/docs/designers-developers/developers/block-api/block-registration.md) of the deprecated form of the block. +- `save` (Function): The [save implementation](/docs/designers-developers/developers/block-api/block-edit-save.md) of the deprecated form of the block. - `migrate` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, is expected to return either the attributes compatible with the deprecated block, or a tuple array of `[ attributes, innerBlocks ]`. - `isEligible` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, returns true if the deprecation can handle the block migration. This is particularly useful in cases where a block is technically valid even once deprecated, and requires updates to its attributes or inner blocks. @@ -275,4 +275,4 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', { In the example above we updated the block to use an inner paragraph block with a title instead of a title attribute. -*Above are example cases of block deprecation. For more, real-world examples, check for deprecations in the [core block library](https://github.com/WordPress/gutenberg/tree/master/packages/block-library/src). Core blocks have been updated across releases and contain simple and complex deprecations.* +*Above are example cases of block deprecation. For more, real-world examples, check for deprecations in the [core block library](/packages/block-library/src/README.md). Core blocks have been updated across releases and contain simple and complex deprecations.* diff --git a/docs/designers-developers/developers/block-api/block-edit-save.md b/docs/designers-developers/developers/block-api/block-edit-save.md index f72baac90858d8..1bfaf08f10a22e 100644 --- a/docs/designers-developers/developers/block-api/block-edit-save.md +++ b/docs/designers-developers/developers/block-api/block-edit-save.md @@ -117,11 +117,11 @@ save() { ``` {% end %} -For most blocks, the return value of `save` should be an [instance of WordPress Element](https://github.com/WordPress/gutenberg/blob/master/packages/element/README.md) representing how the block is to appear on the front of the site. +For most blocks, the return value of `save` should be an [instance of WordPress Element](/packages/element/README.md) representing how the block is to appear on the front of the site. _Note:_ While it is possible to return a string value from `save`, it _will be escaped_. If the string includes HTML markup, the markup will be shown on the front of the site verbatim, not as the equivalent HTML node content. If you must return raw HTML from `save`, use `wp.element.RawHTML`. As the name implies, this is prone to [cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting) and therefore is discouraged in favor of a WordPress Element hierarchy whenever possible. -For [dynamic blocks](../../../../docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md), the return value of `save` could either represent a cached copy of the block's content to be shown only in case the plugin implementing the block is ever disabled. Alternatively, return a `null` (empty) value to save no markup in post content for the dynamic block, instead deferring this to always be calculated when the block is shown on the front of the site. +For [dynamic blocks](/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md), the return value of `save` could either represent a cached copy of the block's content to be shown only in case the plugin implementing the block is ever disabled. Alternatively, return a `null` (empty) value to save no markup in post content for the dynamic block, instead deferring this to always be calculated when the block is shown on the front of the site. ### attributes @@ -171,10 +171,10 @@ The two most common sources of block invalidations are: Before starting to debug, be sure to familiarize yourself with the validation step described above documenting the process for detecting whether a block is invalid. A block is invalid if its regenerated markup does not match what is saved in post content, so often this can be caused by the attributes of a block being parsed incorrectly from the saved content. -If you're using [attribute sources](../../../../docs/designers-developers/developers/block-api/block-attributes.md), be sure that attributes sourced from markup are saved exactly as you expect, and in the correct type (usually a `'string'` or `'number'`). +If you're using [attribute sources](/docs/designers-developers/developers/block-api/block-attributes.md), be sure that attributes sourced from markup are saved exactly as you expect, and in the correct type (usually a `'string'` or `'number'`). When a block is detected as invalid, a warning will be logged into your browser's developer tools console. The warning will include specific details about the exact point at which a difference in markup occurred. Be sure to look closely at any differences in the expected and actual markups to see where problems are occurring. **I've changed my block's `save` behavior and old content now includes invalid blocks. How can I fix this?** -Refer to the guide on [Deprecated Blocks](../../../../docs/designers-developers/developers/block-api/block-deprecations.md) to learn more about how to accommodate legacy content in intentional markup changes. +Refer to the guide on [Deprecated Blocks](/docs/designers-developers/developers/block-api/block-deprecations.md) to learn more about how to accommodate legacy content in intentional markup changes. diff --git a/docs/designers-developers/developers/block-api/block-registration.md b/docs/designers-developers/developers/block-api/block-registration.md index e36628c7249e04..4bc32fb9f4e0fa 100644 --- a/docs/designers-developers/developers/block-api/block-registration.md +++ b/docs/designers-developers/developers/block-api/block-registration.md @@ -66,7 +66,7 @@ The core provided categories are: category: 'widgets', ``` -Plugins and Themes can also register [custom block categories](../docs/extensibility/extending-blocks/#managing-block-categories). +Plugins and Themes can also register [custom block categories](/docs/designers-developers/developers/filters/block-filters.md#managing-block-categories). #### Icon (optional) @@ -82,7 +82,7 @@ icon: 'book-alt', icon: , ``` -**Note:** Custom SVG icons are automatically wrapped in the [`wp.components.SVG` component](https://github.com/WordPress/gutenberg/tree/master/packages/components/src/primitives/svg/) to add accessibility attributes (`aria-hidden`, `role`, and `focusable`). +**Note:** Custom SVG icons are automatically wrapped in the [`wp.components.SVG` component](/packages/components/src/primitives/svg/) to add accessibility attributes (`aria-hidden`, `role`, and `focusable`). An object can also be passed as icon, in this case, icon, as specified above, should be included in the src property. Besides src the object can contain background and foreground colors, this colors will appear with the icon @@ -138,7 +138,7 @@ styles: [ ], ``` -Plugins and Themes can also register [custom block style](../docs/extensibility/extending-blocks/#block-style-variations) for existing blocks. +Plugins and Themes can also register [custom block style](/docs/designers-developers/developers/filters/block-filters.md#block-style-variations) for existing blocks. #### Attributes (optional) @@ -166,7 +166,7 @@ attributes: { }, ``` -* **See: [Attributes](../docs/block-api/attributes.md).** +* **See: [Attributes](/docs/designers-developers/developers/block-api/block-attributes.md).** #### Transforms (optional) @@ -486,7 +486,7 @@ transforms: { * **Type:** `Array` -Blocks are able to be inserted into blocks that use [`InnerBlocks`](https://github.com/WordPress/gutenberg/blob/master/packages/editor/src/components/inner-blocks/README.md) as nested content. Sometimes it is useful to restrict a block so that it is only available as a nested block. For example, you might want to allow an 'Add to Cart' block to only be available within a 'Product' block. +Blocks are able to be inserted into blocks that use [`InnerBlocks`](/packages/editor/src/components/inner-blocks/README.md) as nested content. Sometimes it is useful to restrict a block so that it is only available as a nested block. For example, you might want to allow an 'Add to Cart' block to only be available within a 'Product' block. Setting `parent` lets a block require that it is only available when nested within the specified blocks. @@ -525,7 +525,7 @@ attributes: { } ``` -- `alignWide` (default `true`): This property allows to enable [wide alignment](../docs/extensibility/theme-support.md#wide-alignment) for your theme. To disable this behavior for a single block, set this flag to `false`. +- `alignWide` (default `true`): This property allows to enable [wide alignment](/docs/designers-developers/developers/themes/theme-support.md#wide-alignment) for your theme. To disable this behavior for a single block, set this flag to `false`. ```js // Remove the support for wide alignment. diff --git a/docs/designers-developers/developers/data/README.md b/docs/designers-developers/developers/data/README.md index 5233753c1445e5..7ba7f9264e8bc7 100644 --- a/docs/designers-developers/developers/data/README.md +++ b/docs/designers-developers/developers/data/README.md @@ -1,10 +1,10 @@ # Data Module Reference - - [**core**: WordPress Core Data](../../docs/designers-developers/developers/data/data-core.md) - - [**core/annotations**: Annotations](../../docs/designers-developers/developers/data/data-core-annotations.md) - - [**core/blocks**: Block Types Data](../../docs/designers-developers/developers/data/data-core-blocks.md) - - [**core/editor**: The Editor’s Data](../../docs/designers-developers/developers/data/data-core-editor.md) - - [**core/edit-post**: The Editor’s UI Data](../../docs/designers-developers/developers/data/data-core-edit-post.md) - - [**core/notices**: Notices Data](../../docs/designers-developers/developers/data/data-core-notices.md) - - [**core/nux**: The NUX (New User Experience) Data](../../docs/designers-developers/developers/data/data-core-nux.md) - - [**core/viewport**: The Viewport Data](../../docs/designers-developers/developers/data/data-core-viewport.md) \ No newline at end of file + - [**core**: WordPress Core Data](/docs/designers-developers/developers/data/data-core.md) + - [**core/annotations**: Annotations](/docs/designers-developers/developers/data/data-core-annotations.md) + - [**core/blocks**: Block Types Data](/docs/designers-developers/developers/data/data-core-blocks.md) + - [**core/editor**: The Editor’s Data](/docs/designers-developers/developers/data/data-core-editor.md) + - [**core/edit-post**: The Editor’s UI Data](/docs/designers-developers/developers/data/data-core-edit-post.md) + - [**core/notices**: Notices Data](/docs/designers-developers/developers/data/data-core-notices.md) + - [**core/nux**: The NUX (New User Experience) Data](/docs/designers-developers/developers/data/data-core-nux.md) + - [**core/viewport**: The Viewport Data](/docs/designers-developers/developers/data/data-core-viewport.md) \ No newline at end of file diff --git a/docs/designers-developers/developers/filters/README.md b/docs/designers-developers/developers/filters/README.md index f6da621ac74be6..c110d2ed3a5543 100644 --- a/docs/designers-developers/developers/filters/README.md +++ b/docs/designers-developers/developers/filters/README.md @@ -4,4 +4,4 @@ There are two types of hooks: [Actions](https://developer.wordpress.org/plugins/hooks/actions/) and [Filters](https://developer.wordpress.org/plugins/hooks/filters/). In addition to PHP actions and filters, WordPress also provides a mechanism for registering and executing hooks in JavaScript. This functionality is also available on npm as the [@wordpress/hooks](https://www.npmjs.com/package/@wordpress/hooks) package, for general purpose use. -You can also learn more about both APIs: [PHP](https://codex.wordpress.org/Plugin_API/) and [JavaScript](https://github.com/WordPress/packages/tree/master/packages/hooks). +You can also learn more about both APIs: [PHP](https://codex.wordpress.org/Plugin_API/) and [JavaScript](/packages/tree/master/packages/hooks). diff --git a/docs/designers-developers/developers/filters/block-filters.md b/docs/designers-developers/developers/filters/block-filters.md index cc4f71bb95540e..120abb63fb3853 100644 --- a/docs/designers-developers/developers/filters/block-filters.md +++ b/docs/designers-developers/developers/filters/block-filters.md @@ -113,7 +113,7 @@ wp.hooks.addFilter( ); ``` -_Note:_ This filter must always be run on every page load, and not in your browser's developer tools console. Otherwise, a [block validation](../../../../docs/designers-developers/developers/block-api/block-edit-save.md#validation) error will occur the next time the post is edited. This is due to the fact that block validation occurs by verifying that the saved output matches what is stored in the post's content during editor initialization. So, if this filter does not exist when the editor loads, the block will be marked as invalid. +_Note:_ This filter must always be run on every page load, and not in your browser's developer tools console. Otherwise, a [block validation](/docs/designers-developers/developers/block-api/block-edit-save.md#validation) error will occur the next time the post is edited. This is due to the fact that block validation occurs by verifying that the saved output matches what is stored in the post's content during editor initialization. So, if this filter does not exist when the editor loads, the block will be marked as invalid. #### `blocks.getBlockDefaultClassName` diff --git a/docs/designers-developers/developers/themes/theme-support.md b/docs/designers-developers/developers/themes/theme-support.md index 07ab19f68df7d1..765483eadb798f 100644 --- a/docs/designers-developers/developers/themes/theme-support.md +++ b/docs/designers-developers/developers/themes/theme-support.md @@ -266,7 +266,7 @@ To change the main column width of the editor, add the following CSS to `style-e You can use those editor widths to match those in your theme. You can use any CSS width unit, including `%` or `px`. -Further reading: [Applying Styles with Stylesheets](https://wordpress.org/gutenberg/handbook/blocks/applying-styles-with-stylesheets/). +Further reading: [Applying Styles with Stylesheets](/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md). ## Default block styles diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md b/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md index e905d0b1a9a941..aa785ff85e31f6 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md @@ -124,7 +124,7 @@ There are a few things to notice: ## Live rendering in Gutenberg editor -Gutenberg 2.8 added the [``](https://github.com/WordPress/gutenberg/tree/master/packages/components/src/server-side-render) block which enables rendering to take place on the server using PHP rather than in JavaScript. +Gutenberg 2.8 added the [``](/packages/components/src/server-side-render) block which enables rendering to take place on the server using PHP rather than in JavaScript. *Server-side render is meant as a fallback; client-side rendering in JavaScript is always preferred (client rendering is faster and allows better editor manipulation).* diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/generate-blocks-with-wp-cli.md b/docs/designers-developers/developers/tutorials/block-tutorial/generate-blocks-with-wp-cli.md index 81c3fa0373a82b..b8fc6862dc69dc 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/generate-blocks-with-wp-cli.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/generate-blocks-with-wp-cli.md @@ -5,7 +5,7 @@ It turns out that writing the simplest possible block which contains only static - [zgordon/gutenberg-course](https://github.com/zgordon/gutenberg-course) - a repository for Zac Gordon's Gutenberg Development Course - [ahmadawais/create-guten-block](https://github.com/ahmadawais/create-guten-block) - A zero-configuration developer toolkit for building WordPress Gutenberg block plugins -It might be also a good idea to browse the folder with [all core blocks](https://github.com/WordPress/gutenberg/tree/master/packages/block-library/src) to see how they are implemented. +It might be also a good idea to browse the folder with [all core blocks](/packages/block-library/src) to see how they are implemented. ## WP-CLI @@ -62,7 +62,7 @@ This will generate 4 files inside the `movies` plugin directory. All files conta * Registers all block assets so that they can be enqueued through Gutenberg in * the corresponding context. * - * @see https://wordpress.org/gutenberg/handbook/blocks/writing-your-first-block-type/#enqueuing-block-scripts + * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type/ */ function movie_block_init() { $dir = dirname( __FILE__ ); @@ -109,23 +109,23 @@ add_action( 'init', 'movie_block_init' ); ( function( wp ) { /** * Registers a new block provided a unique name and an object defining its behavior. - * @see https://github.com/WordPress/gutenberg/tree/master/blocks#api + * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/#registering-a-block */ var registerBlockType = wp.blocks.registerBlockType; /** * Returns a new element of given type. Element is an abstraction layer atop React. - * @see https://github.com/WordPress/gutenberg/tree/master/packages/element#element + * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/packages/packages-element/ */ var el = wp.element.createElement; /** * Retrieves the translation of text. - * @see https://github.com/WordPress/gutenberg/tree/master/i18n#api + * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/packages/packages-i18n/ */ var __ = wp.i18n.__; /** * Every block starts by registering a new block type definition. - * @see https://wordpress.org/gutenberg/handbook/block-api/ + * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/#registering-a-block */ registerBlockType( 'movies/movie', { /** @@ -151,7 +151,7 @@ add_action( 'init', 'movie_block_init' ); /** * The edit function describes the structure of your block in the context of the editor. * This represents what the editor will render when the block is used. - * @see https://wordpress.org/gutenberg/handbook/block-edit-save/#edit + * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#edit * * @param {Object} [props] Properties passed from the editor. * @return {Element} Element to render. @@ -167,7 +167,7 @@ add_action( 'init', 'movie_block_init' ); /** * The save function defines the way in which the different attributes should be combined * into the final markup, which is then serialized by Gutenberg into `post_content`. - * @see https://wordpress.org/gutenberg/handbook/block-edit-save/#save + * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#save * * @return {Element} Element to render. */ diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md b/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md index cd2b40e7d561e9..7fa730c73abcee 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md @@ -110,7 +110,7 @@ registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-03', { ``` {% end %} -When registering a new block type, the `attributes` property describes the shape of the attributes object you'd like to receive in the `edit` and `save` functions. Each value is a [source function](../../../../../docs/designers-developers/developers/block-api/block-attributes.md) to find the desired value from the markup of the block. +When registering a new block type, the `attributes` property describes the shape of the attributes object you'd like to receive in the `edit` and `save` functions. Each value is a [source function](/docs/designers-developers/developers/block-api/block-attributes.md) to find the desired value from the markup of the block. In the code snippet above, when loading the editor, we will extract the `content` value as the HTML of the paragraph element in the saved post's markup. diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md index 4a7c8449763bca..ea9a18bbf23684 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md @@ -28,7 +28,7 @@ add_action( 'init', 'gutenberg_boilerplate_block' ); Note the two script dependencies: - __`wp-blocks`__ includes block type registration and related functions -- __`wp-element`__ includes the [WordPress Element abstraction](https://github.com/WordPress/gutenberg/tree/master/packages/element) for describing the structure of your blocks +- __`wp-element`__ includes the [WordPress Element abstraction](/packages/element/README.md) for describing the structure of your blocks If you were to use a component from the `wp-editor` package, for example the RichText component, you would also need to add `wp-editor` to the dependency list. @@ -82,7 +82,7 @@ registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-01', { ``` {% end %} -Once a block is registered, you should immediately see that it becomes available as an option in the editor inserter dialog, using values from `title`, `icon`, and `category` to organize its display. You can choose an icon from any included in the built-in [Dashicons icon set](https://developer.wordpress.org/resource/dashicons/), or provide a [custom svg element](https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-registration/#icon-optional). +Once a block is registered, you should immediately see that it becomes available as an option in the editor inserter dialog, using values from `title`, `icon`, and `category` to organize its display. You can choose an icon from any included in the built-in [Dashicons icon set](https://developer.wordpress.org/resource/dashicons/), or provide a [custom svg element](/docs/designers-developers/developers/block-api/block-registration.md#icon-optional). A block name must be prefixed with a namespace specific to your plugin. This helps prevent conflicts when more than one plugin registers a block with the same name. diff --git a/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md b/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md index 67d703cb37310f..8d175ef3e6d9b2 100644 --- a/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md +++ b/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md @@ -1,6 +1,6 @@ # Extending the Block Editor -Let's look at using the [Block Style Variation example](../../../../../docs/designers-developers/developers/filters/block-filters.md#block-style-variations) to extend the editor. This example allows you to add your own custom CSS class name to any core block type. +Let's look at using the [Block Style Variation example](/docs/designers-developers/developers/filters/block-filters.md#block-style-variations) to extend the editor. This example allows you to add your own custom CSS class name to any core block type. Replace the existing `console.log()` code in your `myguten.js` file with: @@ -30,7 +30,7 @@ add_action( 'enqueue_block_editor_assets', 'myguten_enqueue' ); The last argument in the `wp_enqueue_script()` function is an array of dependencies. WordPress makes packages available under the `wp` namespace. In the example, you use `wp.blocks` to access the items that the blocks package exports (in this case the `registerBlockStyle()` function). -See [Packages](../../../../../docs/designers-developers/developers/packages.md) for list of available packages and what objects they export. +See [Packages](/docs/designers-developers/developers/packages.md) for list of available packages and what objects they export. After you have updated both JavaScript and PHP files, go to the Block Editor and create a new post. diff --git a/docs/designers-developers/developers/tutorials/javascript/readme.md b/docs/designers-developers/developers/tutorials/javascript/readme.md index 11fecdff4d269e..55628c1cd398e4 100644 --- a/docs/designers-developers/developers/tutorials/javascript/readme.md +++ b/docs/designers-developers/developers/tutorials/javascript/readme.md @@ -11,9 +11,9 @@ The Block Editor introduced in WordPress 5.0 is written entirely in JavaScript, ### Table of Contents -1. [Plugins Background](../../../../../docs/designers-developers/developers/tutorials/javascript/plugins-background.md) -2. [Loading JavaScript](../../../../../docs/designers-developers/developers/tutorials/javascript/loading-javascript.md) -3. [Extending the Block Editor](../../../../../docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md) -4. [Troubleshooting](../../../../../docs/designers-developers/developers/tutorials/javascript/troubleshooting.md) -5. [JavaScript Versions and Building](../../../../../docs/designers-developers/developers/tutorials/javascript/versions-and-building.md) -6. [Scope your code](../../../../../docs/designers-developers/developers/tutorials/javascript/scope-your-code.md) \ No newline at end of file +1. [Plugins Background](/docs/designers-developers/developers/tutorials/javascript/plugins-background.md) +2. [Loading JavaScript](/docs/designers-developers/developers/tutorials/javascript/loading-javascript.md) +3. [Extending the Block Editor](/docs/designers-developers/developers/tutorials/javascript/extending-the-block-editor.md) +4. [Troubleshooting](/docs/designers-developers/developers/tutorials/javascript/troubleshooting.md) +5. [JavaScript Versions and Building](/docs/designers-developers/developers/tutorials/javascript/versions-and-building.md) +6. [Scope your code](/docs/designers-developers/developers/tutorials/javascript/scope-your-code.md) diff --git a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md index 3bfc68e09262b6..0ad9966b91a12c 100644 --- a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md +++ b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md @@ -2,7 +2,7 @@ With the meta field registered in the previous step, next you will create a new block used to display the field value to the user. See the [Block Tutorial](/docs/designers-developers/developers/tutorials/block-tutorial/readme.md) for a deeper understanding of creating custom blocks. -For this block, you will use the TextControl component, which is similar to an HTML input text field. For additional components, check out the [components](https://github.com/WordPress/gutenberg/tree/master/packages/components/src) and [editor](https://github.com/WordPress/gutenberg/tree/master/packages/editor/src/components) packages repositories. +For this block, you will use the TextControl component, which is similar to an HTML input text field. For additional components, check out the [components](/packages/components/src) and [editor](/packages/editor/src/components) packages repositories. Attributes are the information displayed in blocks. As shown in the block tutorial, the source of attributes come from the text or HTML a user writes in the editor. For your meta block, the attribute will come from the post meta field. diff --git a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-0.md b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-0.md index 9e7efca41b793c..3ec8bcba59e930 100644 --- a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-0.md +++ b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-0.md @@ -1,12 +1,12 @@ # Creating a sidebar for your plugin -This tutorial starts with you having an existing plugin setup and ready to add PHP and JavaScript code. Please, refer to [Getting started with JavaScript](../../../../../docs/designers-developers/developers/tutorials/javascript/) tutorial for an introduction to WordPress plugins and how to use JavaScript to extend the block editor. +This tutorial starts with you having an existing plugin setup and ready to add PHP and JavaScript code. Please, refer to [Getting started with JavaScript](/docs/designers-developers/developers/tutorials/javascript/) tutorial for an introduction to WordPress plugins and how to use JavaScript to extend the block editor. In the next sections, you're going to create a custom sidebar for a plugin that contains a text control so the user can update a value that is stored in the `post_meta` table. -1. [Get a sidebar up and running](../../../../../docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-1-up-and-running.md) -2. [Tweak the sidebar style and add controls](../../../../../docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-2-styles-and-controls.md) -3. [Register a new meta field](../../../../../docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-3-register-meta.md) -4. [Initialize the input control with the meta field value](../../../../../docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-4-initialize-input.md) -5. [Update the meta field value when input's content changes](../../../../../docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-5-update-meta.md) -6. [Finishing touches](../../../../../docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-6-finishing-touches.md) +1. [Get a sidebar up and running](/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-1-up-and-running.md) +2. [Tweak the sidebar style and add controls](/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-2-styles-and-controls.md) +3. [Register a new meta field](/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-3-register-meta.md) +4. [Initialize the input control with the meta field value](/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-4-initialize-input.md) +5. [Update the meta field value when input's content changes](/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-5-update-meta.md) +6. [Finishing touches](/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-6-finishing-touches.md) diff --git a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-1-up-and-running.md b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-1-up-and-running.md index 54fc46066f2721..6b9c335f335a58 100644 --- a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-1-up-and-running.md +++ b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-1-up-and-running.md @@ -1,6 +1,6 @@ # Get a sidebar up and running -The first step in the journey is to tell the editor that there is a new plugin that will have its own sidebar. You can do so by using the [registerPlugin](../../../../../docs/designers-developers/developers/packages/packages-plugins/), [PluginSidebar](../../../../../docs/designers-developers/developers/packages/packages-edit-post/#pluginsidebar), and [createElement](../../../../../docs/designers-developers/developers/packages/packages-element/) utilities provided by WordPress, to be found in the `@wordpress/plugins`, `@wordpress/edit-post`, and `@wordpress/element` [packages](../../../../../docs/designers-developers/developers/packages/), respectively. +The first step in the journey is to tell the editor that there is a new plugin that will have its own sidebar. You can do so by using the [registerPlugin](/packages/plugins/REAMDE.md), [PluginSidebar](/packages/edit-post/README.md#pluginsidebar), and [createElement](/packages/element/README.md) utilities provided by WordPress, to be found in the `@wordpress/plugins`, `@wordpress/edit-post`, and `@wordpress/element` [packages](/docs/designers-developers/developers/packages.md), respectively. Add the following code to a JavaScript file called `plugin-sidebar.js` and save it within your plugin's directory: diff --git a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-2-styles-and-controls.md b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-2-styles-and-controls.md index 27e05ba9b22164..d919d11c657aed 100644 --- a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-2-styles-and-controls.md +++ b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-2-styles-and-controls.md @@ -2,7 +2,7 @@ After the sidebar is up and running, the next step is to fill it up with the necessary components and basic styling. -To visualize and edit the meta field value you'll use an input component. The `@wordpress/components` package contains many components available for you to reuse, and, specifically, the [TextControl](../../../../../docs/designers-developers/developers/components/text-control/) is aimed at creating an input field: +To visualize and edit the meta field value you'll use an input component. The `@wordpress/components` package contains many components available for you to reuse, and, specifically, the [TextControl](/packages/components/src/text-control/README.md) is aimed at creating an input field: ```js ( function( wp ) { diff --git a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-3-register-meta.md b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-3-register-meta.md index a4d150401e519a..9434ee462fc8bf 100644 --- a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-3-register-meta.md +++ b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-3-register-meta.md @@ -12,7 +12,7 @@ register_meta( 'post', 'sidebar_plugin_meta_block_field', array( ) ); ``` -To make sure the field has been loaded, query the block editor [internal data structures](../../../../../docs/designers-developers/developers/data/), also known as _stores_. Open your browser's console, and execute this piece of code: +To make sure the field has been loaded, query the block editor [internal data structures](/docs/designers-developers/developers/data/), also known as _stores_. Open your browser's console, and execute this piece of code: ```js wp.data.select( 'core/editor' ).getCurrentPost().meta; diff --git a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-4-initialize-input.md b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-4-initialize-input.md index 38e09a2529548c..d826d9df5547cc 100644 --- a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-4-initialize-input.md +++ b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-4-initialize-input.md @@ -39,7 +39,7 @@ Now that the field is available in the editor store, it can be surfaced to the U Now you can focus solely on the `MetaBlockField` component. The goal is to initialize it with the value of `sidebar_plugin_meta_block_field`, but also to keep it updated when that value changes. -WordPress has [some utilities to work with data](../../../../../docs/designers-developers/developers/packages/packages-data/) from the stores. The first you're going to use is [withSelect](../../../../../docs/designers-developers/developers/packages/packages-data/#withselect-mapselecttoprops-function-function), whose signature is: +WordPress has [some utilities to work with data](/packages/data/README.md) from the stores. The first you're going to use is [withSelect](/packages/data/README.md#withselect-mapselecttoprops-function-function), whose signature is: ```js withSelect( @@ -105,7 +105,7 @@ This is how the code changes from the previous section: * The `MetaBlockField` function has now a `props` argument as input. It contains the data object returned by the `mapSelectToProps` function, which it uses to initialize its value property. * The component rendered within the `div` element was also updated, the plugin now uses `MetaBlockFieldWithData`. This will be updated every time the original data changes. -* [getEditedPostAttribute](../../../../../docs/designers-developers/developers/data/data-core-editor/#geteditedpostattribute) is used to retrieve data instead of [getCurrentPost](../../../../../docs/designers-developers/developers/data/data-core-editor/#getcurrentpost) because it returns the most recent values of the post, including user editions that haven't been yet saved. +* [getEditedPostAttribute](/docs/designers-developers/developers/data/data-core-editor.md#geteditedpostattribute) is used to retrieve data instead of [getCurrentPost](/docs/designers-developers/developers/data/data-core-editor.md#getcurrentpost) because it returns the most recent values of the post, including user editions that haven't been yet saved. Update the code and open the sidebar. The input's content is no longer `Initial value` but a void string. Users can't type values yet, but let's check that the component is updated if the value in the store changes. Open the browser's console, execute diff --git a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-5-update-meta.md b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-5-update-meta.md index 0cb56a29b7e852..0ccab7e115b18f 100644 --- a/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-5-update-meta.md +++ b/docs/designers-developers/developers/tutorials/sidebar-tutorial/plugin-sidebar-5-update-meta.md @@ -1,6 +1,6 @@ # Update the meta field when the input's content changes -The last step in the journey is to update the meta field when the input content changes. To do that, you'll use another utility from the `@wordpress/data` package, [withDispatch](../../../../../docs/designers-developers/developers/packages/packages-data/#withdispatch-mapdispatchtoprops-function-function). +The last step in the journey is to update the meta field when the input content changes. To do that, you'll use another utility from the `@wordpress/data` package, [withDispatch](/packages/data/README.md#withdispatch-mapdispatchtoprops-function-function). `withDispatch` works similarly to `withSelect`. It takes two functions, the first returns an object with data, and the second takes that data object as input and returns a new UI component. Let's see how to use it: diff --git a/docs/designers-developers/faq.md b/docs/designers-developers/faq.md index fb2d16dfe68fce..1f54d5d78e7ea8 100644 --- a/docs/designers-developers/faq.md +++ b/docs/designers-developers/faq.md @@ -251,7 +251,7 @@ Our [list of supported browsers can be found in the Make WordPress handbook](htt ## How do I make my own block? -The API for creating blocks is a crucial aspect of the project. We are working on improved documentation and tutorials. Check out the [Creating Block Types](../../docs/designers-developers/developers/tutorials/block-tutorial/readme.md) section to get started. +The API for creating blocks is a crucial aspect of the project. We are working on improved documentation and tutorials. Check out the [Creating Block Types](/docs/designers-developers/developers/tutorials/block-tutorial/readme.md) section to get started. ## Does Gutenberg involve editing posts/pages in the front-end? @@ -295,7 +295,7 @@ Blocks will be able to provide base structural CSS styles, and themes can add st Other features, like the new _wide_ and _full-wide_ alignment options, will simply be CSS classes applied to blocks that offer this alignment. We are looking at how a theme can opt in to this feature, for example using `add_theme_support`. -*See:* [Theme Support](../../docs/designers-developers/developers/themes/theme-support.md) +*See:* [Theme Support](/docs/designers-developers/developers/themes/theme-support.md) ## How will editor styles work? @@ -308,7 +308,7 @@ function gutenbergtheme_editor_styles() { add_action( 'enqueue_block_editor_assets', 'gutenbergtheme_editor_styles' ); ``` -*See:* [Editor Styles](../../docs/designers-developers/developers/themes/theme-support.md#editor-styles) +*See:* [Editor Styles](/docs/designers-developers/developers/themes/theme-support.md#editor-styles) ## Should I be concerned that Gutenberg will make my plugin obsolete? @@ -353,7 +353,7 @@ Our approach—as outlined in [the technical overview introduction](https://make This also [gives us the flexibility](https://github.com/WordPress/gutenberg/issues/1516) to store those blocks that are inherently separate from the content stream (reusable pieces like widgets or small post type elements) elsewhere, and just keep token references for their placement. -We suggest you look at the [Gutenberg key concepts](../../docs/designers-developers/key-concepts.md) to learn more about how this aspect of the project works. +We suggest you look at the [Gutenberg key concepts](/docs/designers-developers/key-concepts.md) to learn more about how this aspect of the project works. ## How can I parse the post content back out into blocks in PHP or JS? In JS: diff --git a/docs/designers-developers/key-concepts.md b/docs/designers-developers/key-concepts.md index ff9007185fe0e7..3252337ba2b339 100644 --- a/docs/designers-developers/key-concepts.md +++ b/docs/designers-developers/key-concepts.md @@ -123,7 +123,7 @@ After running this through the parser we're left with a simple object we can man This has dramatic implications for how simple and performant we can make our parser. These explicit boundaries also protect damage in a single block from bleeding into other blocks or tarnishing the entire document. It also allows the system to identify unrecognized blocks before rendering them. -_N.B.:_ The defining aspect of blocks are their semantics and the isolation mechanism they provide; in other words, their identity. On the other hand, where their data is stored is a more liberal aspect. Blocks support more than just static local data (via JSON literals inside the HTML comment or within the block's HTML), and more mechanisms (_e.g._, global blocks or otherwise resorting to storage in complementary `WP_Post` objects) are expected. See [attributes](../../docs/designers-developers/developers/block-api/block-attributes.md) for details. +_N.B.:_ The defining aspect of blocks are their semantics and the isolation mechanism they provide; in other words, their identity. On the other hand, where their data is stored is a more liberal aspect. Blocks support more than just static local data (via JSON literals inside the HTML comment or within the block's HTML), and more mechanisms (_e.g._, global blocks or otherwise resorting to storage in complementary `WP_Post` objects) are expected. See [attributes](/docs/designers-developers/developers/block-api/block-attributes.md) for details. ## The Anatomy of a Serialized Block diff --git a/docs/readme.md b/docs/readme.md index 5164003a654a87..eb699e6a2ed08f 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -6,7 +6,7 @@ The Gutenberg project provides three sources of documentation: Learn how to build blocks and extend the editor, best practices for designing block interfaces, and how to create themes that make the most of the new features Gutenberg provides. -[Visit the Designer & Developer Handbook](../docs/designers-developers/readme.md) +[Visit the Designer & Developer Handbook](/docs/designers-developers/readme.md) ## User Handbook @@ -17,4 +17,4 @@ Discover the new features Gutenberg offers, learn how your site will be affected Help make Gutenberg better by contributing ideas, code, testing, and more. -[Visit the Contributor Handbook](../docs/contributors/readme.md) +[Visit the Contributor Handbook](/docs/contributors/readme.md) diff --git a/docs/tool/generator.js b/docs/tool/generator.js index a632fb40b52681..f5ee6f584e78f7 100644 --- a/docs/tool/generator.js +++ b/docs/tool/generator.js @@ -17,7 +17,7 @@ function generateTableOfContent( parsedNamespaces ) { '# Data Module Reference', '', Object.values( parsedNamespaces ).map( ( parsedNamespace ) => { - return ` - [**${ parsedNamespace.name }**: ${ parsedNamespace.title }](../../docs/designers-developers/developers/data/data-${ kebabCase( parsedNamespace.name ) }.md)`; + return ` - [**${ parsedNamespace.name }**: ${ parsedNamespace.title }](/docs/designers-developers/developers/data/data-${ kebabCase( parsedNamespace.name ) }.md)`; } ).join( '\n' ), ].join( '\n' ); } diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index cebec43084b1ee..18ef8d72b238ac 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -2,7 +2,7 @@ Thank you for taking the time to contribute. -The following is a set of guidelines for contributing to the `@wordpress/components` package to be considered in addition to the general ones described in our [Contributing Policy](../../CONTRIBUTING.md). +The following is a set of guidelines for contributing to the `@wordpress/components` package to be considered in addition to the general ones described in our [Contributing Policy](/CONTRIBUTING.md). ## Examples From a00c7f4b028c556e38d174706b4e2b3fb35e96bc Mon Sep 17 00:00:00 2001 From: Ajit Bohra Date: Tue, 29 Jan 2019 14:40:24 +0530 Subject: [PATCH 10/36] Docs: clarify isDefault usage for registerBlockStyles() (#11478) * Docs: Remove isDefault from registerBlockStyle * Docs: clarify isDefault usage for registerBlockStyles() * Clarify isDefault Co-Authored-By: ajitbohra --- docs/designers-developers/developers/filters/block-filters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/designers-developers/developers/filters/block-filters.md b/docs/designers-developers/developers/filters/block-filters.md index 120abb63fb3853..99ba4cc0a0d49f 100644 --- a/docs/designers-developers/developers/filters/block-filters.md +++ b/docs/designers-developers/developers/filters/block-filters.md @@ -17,7 +17,7 @@ wp.blocks.registerBlockStyle( 'core/quote', { The example above registers a block style variation named `fancy-quote` to the `core/quote` block. When the user selects this block style variation from the styles selector, an `is-style-fancy-quote` className will be added to the block's wrapper. -By adding `isDefault: true`, you can make registered style variation to be active by default when a block is inserted. +By adding `isDefault: true` you can mark the registered style variation as the one that is recognized as active when no custom class name is provided. It also means that there will be no custom class name added to the HTML output for the style that is marked as default. To remove a block style variation use `wp.blocks.unregisterBlockStyle()`. From 190ce7f8cd0076305bec4641ff555072c0f438c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Tue, 29 Jan 2019 10:14:54 +0100 Subject: [PATCH 11/36] Fix wp-settings permissions (#13539) --- bin/install-wordpress.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/install-wordpress.sh b/bin/install-wordpress.sh index 40eec0810bd90e..a5ba87ec4f4d5a 100755 --- a/bin/install-wordpress.sh +++ b/bin/install-wordpress.sh @@ -65,6 +65,7 @@ fi # Make sure the uploads and upgrade folders exist and we have permissions to add files. echo -e $(status_message "Ensuring that files can be uploaded...") docker-compose $DOCKER_COMPOSE_FILE_OPTIONS run --rm $CONTAINER chmod 767 /var/www/html/wp-content/plugins +docker-compose $DOCKER_COMPOSE_FILE_OPTIONS run --rm $CONTAINER chmod 767 /var/www/html/wp-settings.php docker-compose $DOCKER_COMPOSE_FILE_OPTIONS run --rm $CONTAINER mkdir -p /var/www/html/wp-content/uploads docker-compose $DOCKER_COMPOSE_FILE_OPTIONS run --rm $CONTAINER chmod -v 767 /var/www/html/wp-content/uploads docker-compose $DOCKER_COMPOSE_FILE_OPTIONS run --rm $CONTAINER mkdir -p /var/www/html/wp-content/upgrade From 5bdc8c612b783a877a84a80521d0cc9280b4f6a4 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 29 Jan 2019 10:06:28 +0000 Subject: [PATCH 12/36] Fix: Add button styles to notice actions without url. Allow custom classes on notice actions. (#13116) --- packages/components/src/notice/README.md | 2 +- packages/components/src/notice/index.js | 40 ++++++++++++++----- packages/components/src/notice/style.scss | 3 ++ .../notice/test/__snapshots__/index.js.snap | 1 + 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/packages/components/src/notice/README.md b/packages/components/src/notice/README.md index c32bf02a3de100..9b0b62937b7f84 100644 --- a/packages/components/src/notice/README.md +++ b/packages/components/src/notice/README.md @@ -31,4 +31,4 @@ The following props are used to control the display of the component. * `status`: (string) can be `warning` (yellow), `success` (green), `error` (red). * `onRemove`: function called when dismissing the notice * `isDismissible`: (boolean) defaults to true, whether the notice should be dismissible or not -* `actions`: (array) an array of action objects. Each member object should contain a `label` and either a `url` link string or `onClick` callback function. +* `actions`: (array) an array of action objects. Each member object should contain a `label` and either a `url` link string or `onClick` callback function. A `className` property can be used to add custom classes to the button styles. By default, some classes are used (e.g: is-link or is-default) the default classes can be removed by setting property `noDefaultClasses` to `false`. diff --git a/packages/components/src/notice/index.js b/packages/components/src/notice/index.js index 90b2d908deee92..99d5f9968f1111 100644 --- a/packages/components/src/notice/index.js +++ b/packages/components/src/notice/index.js @@ -36,17 +36,35 @@ function Notice( {
{ children } - { actions.map( ( { label, url, onClick }, index ) => ( - - ) ) } + { actions.map( + ( + { + className: buttonCustomClasses, + label, + noDefaultClasses = false, + onClick, + url, + }, + index + ) => { + return ( + + ); + } + + ) }
{ isDismissible && ( View From a13d933513a4a7c31ded5cc16c78f1204d7969d7 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 29 Jan 2019 10:26:39 +0000 Subject: [PATCH 13/36] Fix: Hide empty categories in categories block. (#13549) This commit uses hide_empty option during categories request to make sure categories that have no posts are not displayed in the categories block edit view. On the frontend, they are not rendered so the edit view should replicate that. Some categories may still show zero as a count. That happens because these categories have descendant categories with posts associated to them. --- packages/block-library/src/categories/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/categories/edit.js b/packages/block-library/src/categories/edit.js index 5341ad6ff51682..ad1dea3ec55bf4 100644 --- a/packages/block-library/src/categories/edit.js +++ b/packages/block-library/src/categories/edit.js @@ -201,7 +201,7 @@ export default compose( withSelect( ( select ) => { const { getEntityRecords } = select( 'core' ); const { isResolving } = select( 'core/data' ); - const query = { per_page: -1 }; + const query = { per_page: -1, hide_empty: true }; return { categories: getEntityRecords( 'taxonomy', 'category', query ), From dc68e4bd7818e423d9abc68224a94235bc3c8d7e Mon Sep 17 00:00:00 2001 From: Sheri Bigelow Date: Tue, 29 Jan 2019 03:34:14 -0700 Subject: [PATCH 14/36] Quick refresh of the Repository Management doc (#13495) * Quick refresh of the Repository Management doc This PR seeks to update the Repository Management documentation to reflect recent practices including the following: - Reorganize the labels section. - General updates to the triage section. - Add a note about how to handle help requests. - Update common examples of labels, milestones, and projects. - Change 'backlog' to 'list'. * Quick refresh of the Repository Management doc Updating based on feedback received: - Be less specific about the version numbers in example milestones. - Add more guidelines about the Needs More Info label. - Mention "Good First Issue" and "Good First Review" labels. * Update repository-management.md --- docs/contributors/repository-management.md | 72 +++++++++++----------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/docs/contributors/repository-management.md b/docs/contributors/repository-management.md index d4e8df0ebf6aa7..08b47e1777f2a2 100644 --- a/docs/contributors/repository-management.md +++ b/docs/contributors/repository-management.md @@ -1,6 +1,6 @@ # Repository Management -The goal is for this to be a living document explaining how we collaboratively manage the Gutenberg repository. If you’d like to suggest a change, please open an issue for discussion or submit a pull request to the document. +This is a living document explaining how we collaboratively manage the Gutenberg repository. If you’d like to suggest a change, please open an issue for discussion or submit a pull request to the document. This document covers: @@ -17,14 +17,24 @@ This document covers: ## Issues -A healthy issue backlog is one where issues are relevant and actionable. *Relevant* in the sense that they relate to the project’s current priorities. *Actionable* in the sense that it’s clear what action(s) need to be taken to resolve the issue. +A healthy issue list is one where issues are relevant and actionable. *Relevant* in the sense that they relate to the project’s current priorities. *Actionable* in the sense that it’s clear what action(s) need to be taken to resolve the issue. -Any issues that are irrelevant or not actionable should be closed, because they get in the way of making progress on the project. Imagine the issue backlog as a desk: the more clutter you have on it, the more difficult it is to use the space to get work done. +Any issues that are irrelevant or not actionable should be closed, because they get in the way of making progress on the project. Imagine the issue list as a desk: the more clutter you have on it, the more difficult it is to use the space to get work done. ### Labels -To better organize the issue backlog, all issues should have [one or more labels](https://github.com/WordPress/gutenberg/labels). Here are some you might commonly see: +All issues should have [one or more labels](https://github.com/WordPress/gutenberg/labels). +Workflow labels start with “Needs” and may be applied as needed. Ideally, each workflow label will have a group that follows it, such as the Accessibility Team for `Needs Accessibility Feedback`, the Testing Team for `Needs Testing`, etc. + +[Priority High](https://github.com/WordPress/gutenberg/labels/Priority%20High) and [Priority OMGWTFBBQ](https://github.com/WordPress/gutenberg/labels/Priority%20OMGWTFBBQ) issues should have an assignee and/or be in an active milestone. + +Help requests or 'how to' questions should be posted in a relevant support forum as a first step. If something might be a bug but it's not clear, the Support Team or a forum volunteer can help troubleshoot the case to help get all the right information needed for an effective bug report. + +Here are some labels you might commonly see: + +- [Good First Issue](https://github.com/WordPress/gutenberg/labels/Good%20First%20Issue) - Issues identified as good for new contributors to work on. Comment to note that you intend to work on the issue and reference the issue number in the pull request you submit. +- [Good First Review](https://github.com/WordPress/gutenberg/labels/Good%20First%20Review) - Pull requests identified as good for new contributors who are interested in doing code reviews. - [Needs Accessibility Feedback](https://github.com/WordPress/gutenberg/labels/Accessibility) - Changes that impact accessibility and need corresponding review (e.g. markup changes). - [Needs Design Feedback](https://github.com/WordPress/gutenberg/labels/Needs%20Design%20Feedback) - Changes that modify the design or user experience in some way and need sign-off. - [[Type] Bug](https://github.com/WordPress/gutenberg/labels/%5BType%5D%20Bug) - An existing feature is broken in some way. @@ -32,53 +42,41 @@ To better organize the issue backlog, all issues should have [one or more labels - [[Type] Plugin / Extension Conflict](https://github.com/WordPress/gutenberg/labels/%5BType%5D%20Plugin%20%2F%20Extension%20Conflict) - Documentation of a conflict between Gutenberg and a plugin or extension. The plugin author should be informed and provided documentation on how to address. - [[Status] Needs More Info](https://github.com/WordPress/gutenberg/labels/%5BStatus%5D%20Needs%20More%20Info) - The issue needs more information in order to be actionable and relevant. Typically this requires follow-up from the original reporter. -Workflow labels may be applied as needed and start with “Needs”. Ideally, each workflow label will have a group that follows it, such as the Accessibility Team for `Needs Accessibility Feedback`, the Testing Team for `Needs Testing`, etc. - -`Priority High` and `Priority OMGWTFBBQ` issues should have an assignee and/or be in an active milestone. - [Check out the label directory](https://github.com/WordPress/gutenberg/labels) for a listing of all labels. ### Milestones -We put issues into [milestones](https://github.com/wordpress/gutenberg/milestones) to better categorize them. Here are some you might see: - -- The next 2 releases we have milestones for (e.g. 2.2, 2.3). -- [Feature Complete](https://github.com/WordPress/gutenberg/milestone/8): This includes big features and is what will be managing the vision of Gutenberg. All of this would be done before even merge proposal is thought about. Examples here include nesting, drag and drop and extensibility API. -- [Merge Proposal: Editor](https://github.com/WordPress/gutenberg/milestone/22): All issues related to merge proposal for the editor. -- [Merge Proposal: Rest API](https://github.com/WordPress/gutenberg/milestone/39): All issues related to merge proposal for the Rest API -- [Merge Proposal: Accessibility](https://github.com/WordPress/gutenberg/milestone/43): All accessibility issues related to merge proposal. -- [Merge Proposal: Media](https://github.com/WordPress/gutenberg/milestone/42): All issues related to merge proposal for the media component. -- [Merge Proposal: Documentation](https://github.com/WordPress/gutenberg/milestone/50): All issues related to documentation for the merge proposal. -- [Merge Proposal: i18n](https://github.com/WordPress/gutenberg/milestone/49): All translation issues for the merge proposal. -- [Merge Proposal: Customization](https://github.com/WordPress/gutenberg/milestone/44): All Customization issues for the merge proposal. -- [Merge Proposal: Plugin](https://github.com/WordPress/gutenberg/milestone/48): All plugin and extensibility issues for the merge proposal. -- [Merge Proposal: Back Compat](https://github.com/WordPress/gutenberg/milestone/47): All back compatibility issues for the merge proposal. -- [Merge Proposal: Themes](https://github.com/WordPress/gutenberg/milestone/48): All theme issues for the merge proposal. -- [Merge Proposal: Core](https://github.com/WordPress/gutenberg/milestone/45): All core issues for the merge proposal that don't fit other merge proposal milestones. -- [Bonus Features](https://github.com/WordPress/gutenberg/milestone/32): Again likely not part of triage and includes nice to haves for the project, if time before merge. A few examples include collaborative editing and footnotes. +We put issues into [milestones](https://github.com/wordpress/gutenberg/milestones) to better categorize them. Issues are added to milestones starting with `WordPress` and pull requests are added to milestones ending in `(Gutenberg)`. + +Here are some milestones you might see: + +- [WordPress X.Y](https://github.com/WordPress/gutenberg/milestone/70): Tasks that should be done for future WordPress releases. +- [X.Y (Gutenberg)](https://github.com/WordPress/gutenberg/milestone/85): PRs targeted for the Gutenberg Plugin X.Y release. - [Future](https://github.com/WordPress/gutenberg/milestone/35): this is something that is confirmed by everyone as a good thing but doesn’t fall into other criteria. ### Triaging Issues -To keep the issue backlog healthy, it needs to be triaged regularly. *Triage* is the practice of reviewing existing issues to make sure they’re relevant, actionable, and have all the information they need. +To keep the issue list healthy, it needs to be triaged regularly. *Triage* is the practice of reviewing existing issues to make sure they’re relevant, actionable, and have all the information they need. -Anyone can help triage the backlog, although you’ll need contributor permission on the Gutenberg repository to modify an issue’s labels or edit its title. +Anyone can help triage, although you’ll need contributor permission on the Gutenberg repository to modify an issue’s labels or edit its title. Here are a couple places you can start: -- [All Gutenberg issues without an assigned label](https://github.com/wordpress/gutenberg/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc+no%3Alabel) -- [The least recently updated Gutenberg issues](https://github.com/WordPress/gutenberg/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc) +- [All Gutenberg issues without an assigned label](https://github.com/wordpress/gutenberg/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc+no%3Alabel). +- [The least recently updated Gutenberg issues](https://github.com/WordPress/gutenberg/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc). -When reviewing the issue backlog, here are some steps you can perform: +When reviewing issues, here are some steps you can perform: -- If it’s a bug report, test to confirm the report. If there is not enough information to confirm the report, add the `[Status] Needs More Info` label. -- If the issue is missing labels, add some to better categorize it. -- If the issue is duplicate of another already in the backlog, close the issue by commenting with “Duplicate of #”. Add any relevant new details to the existing issue. +- First search for duplicates. If the issue is duplicate, close it by commenting with “Duplicate of #” and add any relevant new details to the existing issue. +- If the issue is missing labels, add some to better categorize it (requires proper permissions). +- If the title doesn’t communicate the issue, edit it for clarity (requires proper permissions). +- If it’s a bug report, test to confirm the report or add the `Needs Testing` label. If there is not enough information to confirm the report, add the `[Status] Needs More Info` label and ask for the details needed. +- Remove the `[Status] Needs More Info` if the author of the issue has responded with enough details. +- Close the issue with a note if it has a `[Status] Needs More Info` label but the author didn't respond in 2+ weeks. - If there was conversation on the issue but no actionable steps identified, follow up with the participants to see what’s actionable. -- If the title doesn’t communicate the issue, edit it for clarity. - If you feel comfortable triaging the issue further, then you can also: - Check that the bug report is valid by debugging it to see if you can track down the technical specifics. - - Check if the issue is missing some detail and see if you can fill in those details. For instance, if a bug report is missing visual detail, it’s helpful to reproduce the issue locally and upload a GIF. + - Check if the issue is missing some detail and see if you can fill in those details. For instance, if a bug report is missing visual detail, it’s helpful to reproduce the issue locally and upload a screenshot or GIF. ## Pull Requests @@ -158,6 +156,6 @@ We use [GitHub projects](https://github.com/WordPress/gutenberg/projects) to kee Some key projects include: -* [Customization](https://github.com/WordPress/gutenberg/projects/13) - Blocks and tasks needed for customization in Gutenberg. -* [Extensibility](https://github.com/WordPress/gutenberg/projects/14) - Comprises the entirety of extensibility APIs. See [Native Gutenberg Extensibility Overview](https://github.com/WordPress/gutenberg/issues/3330) for more details. -* [Third-Party Compatibility](https://github.com/WordPress/gutenberg/projects/15) - Issue that impact Gutenberg's adoption in the real world. +* [Phase 2](https://github.com/WordPress/gutenberg/projects/13) - Development tasks needed for Phase 2 of Gutenberg. +* [Phase 2 design](https://github.com/WordPress/gutenberg/projects/21) - Tasks for design in Phase 2. Note: specific projects may have their own boards. +* [Ideas](https://github.com/WordPress/gutenberg/projects/8) - Project containing tickets that, while closed for the time being, can be revisited in the future. From b35774d56866a6d54db04a396f13494c06c3069d Mon Sep 17 00:00:00 2001 From: Kuba Birecki Date: Tue, 29 Jan 2019 11:43:09 +0100 Subject: [PATCH 15/36] Replace Polldaddy embed block with Crowdsignal (#12854) * Replace Polldaddy embed block with Crowdsignal * Remove Polldaddy from the inserter * Update core-embeds.js * Update core-embeds.js * Update core-embeds.js --- .../block-library/src/embed/core-embeds.js | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/embed/core-embeds.js b/packages/block-library/src/embed/core-embeds.js index 9fe6c2b335cd85..7edb114dae9ba2 100644 --- a/packages/block-library/src/embed/core-embeds.js +++ b/packages/block-library/src/embed/core-embeds.js @@ -145,6 +145,25 @@ export const others = [ }, patterns: [ /^https?:\/\/(www\.)?collegehumor\.com\/.+/i ], }, + { + name: 'core-embed/crowdsignal', + settings: { + title: 'Crowdsignal', + icon: embedContentIcon, + keywords: [ 'polldaddy' ], + transform: [ { + type: 'block', + blocks: [ 'core-embed/polldaddy' ], + transform: ( content ) => { + return createBlock( 'core-embed/crowdsignal', { + content, + } ); + }, + } ], + description: __( 'Embed Crowdsignal (formerly Polldaddy) content.' ), + }, + patterns: [ /^https?:\/\/((.+\.)?polldaddy\.com|poll\.fm|.+\.survey\.fm)\/.+/i ], + }, { name: 'core-embed/dailymotion', settings: { @@ -210,13 +229,17 @@ export const others = [ patterns: [ /^https?:\/\/(www\.)?mixcloud\.com\/.+/i ], }, { + // Deprecated in favour of the core-embed/crowdsignal block name: 'core-embed/polldaddy', settings: { title: 'Polldaddy', icon: embedContentIcon, description: __( 'Embed Polldaddy content.' ), + supports: { + inserter: false, + }, }, - patterns: [ /^https?:\/\/(www\.)?polldaddy\.com\/.+/i ], + patterns: [], }, { name: 'core-embed/reddit', From cd63614338d6b7646aa3bd4be76bac0bd876c542 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 29 Jan 2019 12:00:09 +0100 Subject: [PATCH 16/36] Clarify multi-selection focus behavior (#11247) * Clarify multi-selection focus behavior * Update block.js --- packages/editor/src/components/block-list/block.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/components/block-list/block.js b/packages/editor/src/components/block-list/block.js index 87d9d117957670..82e7154b76995b 100644 --- a/packages/editor/src/components/block-list/block.js +++ b/packages/editor/src/components/block-list/block.js @@ -99,8 +99,9 @@ export class BlockListBlock extends Component { this.focusTabbable( true ); } - // When triggering a multi-selection, - // move the focus to the wrapper of the first selected block. + // When triggering a multi-selection, move the focus to the wrapper of the first selected block. + // This ensures that it is not possible to continue editing the initially selected block + // when a multi-selection is triggered. if ( this.props.isFirstMultiSelected && ! prevProps.isFirstMultiSelected ) { this.wrapperNode.focus(); } @@ -301,6 +302,8 @@ export class BlockListBlock extends Component { deleteOrInsertAfterWrapper( event ) { const { keyCode, target } = event; + // These block shortcuts should only trigger if the wrapper of the block is selected + // And when it's not a multi-selection to avoid conflicting with RichText/Inputs and multiselection. if ( ! this.props.isSelected || target !== this.wrapperNode || From 6d500c2085513f490f87b66c9bff01b91fd07b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?= Date: Tue, 29 Jan 2019 12:12:03 +0100 Subject: [PATCH 17/36] Scripts: Remove npm run build from test-e2e default run (#13420) --- .travis.yml | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 55fd93ba4a1efc..8a464d766d9739 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,6 +68,7 @@ jobs: - ./bin/setup-local-env.sh script: - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests + - npm run build - npm run test-e2e -- --ci --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 2 == 0' < ~/.jest-e2e-tests ) - name: E2E tests (Admin with plugins) (2/2) @@ -76,6 +77,7 @@ jobs: - ./bin/setup-local-env.sh script: - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests + - npm run build - npm run test-e2e -- --ci --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 2 == 1' < ~/.jest-e2e-tests ) - name: E2E tests (Author without plugins) (1/2) @@ -84,6 +86,7 @@ jobs: - ./bin/setup-local-env.sh script: - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests + - npm run build - npm run test-e2e -- --ci --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 2 == 0' < ~/.jest-e2e-tests ) - name: E2E tests (Author without plugins) (2/2) @@ -92,4 +95,5 @@ jobs: - ./bin/setup-local-env.sh script: - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests + - npm run build - npm run test-e2e -- --ci --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 2 == 1' < ~/.jest-e2e-tests ) diff --git a/package.json b/package.json index c59a6c200a189d..136d1db76679fb 100644 --- a/package.json +++ b/package.json @@ -177,7 +177,7 @@ "publish:dev": "npm run build:packages && lerna publish --npm-tag next", "publish:prod": "npm run build:packages && lerna publish", "test": "npm run lint && npm run test-unit", - "pretest-e2e": "concurrently \"./bin/reset-e2e-tests.sh\" \"npm run build\"", + "pretest-e2e": "./bin/reset-e2e-tests.sh", "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", "test-e2e:watch": "npm run test-e2e -- --watch", "test-php": "npm run lint-php && npm run test-unit-php", From ccbf07e3dcd9990284a9b8014537917772ec5c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20Van=C2=A0Durpe?= Date: Tue, 29 Jan 2019 13:00:06 +0100 Subject: [PATCH 18/36] RichText: List: Fix outdent with children (#13559) * Fix outdent * Add unit test * Add e2e test * Add unit tests for getLastChildIndex --- .../blocks/__snapshots__/list.test.js.snap | 12 +++++ packages/e2e-tests/specs/blocks/list.test.js | 18 +++++++ .../rich-text/src/get-last-child-index.js | 40 +++++++++++++++ packages/rich-text/src/outdent-list-items.js | 21 +++++--- .../src/test/get-last-child-index.js | 50 +++++++++++++++++++ .../rich-text/src/test/outdent-list-items.js | 30 +++++++++-- 6 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 packages/rich-text/src/get-last-child-index.js create mode 100644 packages/rich-text/src/test/get-last-child-index.js diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap index a4cb6cdb75be57..71118e8d65118c 100644 --- a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap +++ b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap @@ -144,6 +144,18 @@ exports[`List should indent and outdent level 2 3`] = ` " `; +exports[`List should outdent with children 1`] = ` +" +
  • a
    • b
      • c
+" +`; + +exports[`List should outdent with children 2`] = ` +" +
  • a
  • b
    • c
+" +`; + exports[`List should split indented list item 1`] = ` "
  • one
    • two
    • three
diff --git a/packages/e2e-tests/specs/blocks/list.test.js b/packages/e2e-tests/specs/blocks/list.test.js index 022c0b88b13362..98b7f74bd25362 100644 --- a/packages/e2e-tests/specs/blocks/list.test.js +++ b/packages/e2e-tests/specs/blocks/list.test.js @@ -262,4 +262,22 @@ describe( 'List', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'should outdent with children', async () => { + await insertBlock( 'List' ); + await page.keyboard.type( 'a' ); + await page.keyboard.press( 'Enter' ); + await pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( 'b' ); + await page.keyboard.press( 'Enter' ); + await pressKeyWithModifier( 'primary', 'm' ); + await page.keyboard.type( 'c' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'ArrowUp' ); + await pressKeyWithModifier( 'primaryShift', 'm' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } ); diff --git a/packages/rich-text/src/get-last-child-index.js b/packages/rich-text/src/get-last-child-index.js new file mode 100644 index 00000000000000..976051b10c0a3e --- /dev/null +++ b/packages/rich-text/src/get-last-child-index.js @@ -0,0 +1,40 @@ +/** + * Internal dependencies + */ + +import { LINE_SEPARATOR } from './special-characters'; + +/** + * Gets the line index of the last child in the list. + * + * @param {Object} value Value to search. + * @param {number} lineIndex Line index of a list item in the list. + * + * @return {Array} The index of the last child. + */ +export function getLastChildIndex( { text, formats }, lineIndex ) { + const lineFormats = formats[ lineIndex ] || []; + // Use the given line index in case there are no next children. + let childIndex = lineIndex; + + // `lineIndex` could be `undefined` if it's the first line. + for ( let index = lineIndex || 0; index < text.length; index++ ) { + // We're only interested in line indices. + if ( text[ index ] !== LINE_SEPARATOR ) { + continue; + } + + const formatsAtIndex = formats[ index ] || []; + + // If the amout of formats is equal or more, store it, then return the + // last one if the amount of formats is less. + if ( formatsAtIndex.length >= lineFormats.length ) { + childIndex = index; + } else { + return childIndex; + } + } + + // If the end of the text is reached, return the last child index. + return childIndex; +} diff --git a/packages/rich-text/src/outdent-list-items.js b/packages/rich-text/src/outdent-list-items.js index 78036aae165e1b..b26587f3a0bd13 100644 --- a/packages/rich-text/src/outdent-list-items.js +++ b/packages/rich-text/src/outdent-list-items.js @@ -6,6 +6,7 @@ import { LINE_SEPARATOR } from './special-characters'; import { normaliseFormats } from './normalise-formats'; import { getLineIndex } from './get-line-index'; import { getParentLineIndex } from './get-parent-line-index'; +import { getLastChildIndex } from './get-last-child-index'; /** * Outdents any selected list items if possible. @@ -16,17 +17,23 @@ import { getParentLineIndex } from './get-parent-line-index'; */ export function outdentListItems( value ) { const { text, formats, start, end } = value; - const lineIndex = getLineIndex( value ); - const lineFormats = formats[ lineIndex ]; + const startingLineIndex = getLineIndex( value, start ); - if ( lineFormats === undefined ) { + // Return early if the starting line index cannot be further outdented. + if ( formats[ startingLineIndex ] === undefined ) { return value; } const newFormats = formats.slice( 0 ); - const parentFormats = formats[ getParentLineIndex( value, lineIndex ) ] || []; - - for ( let index = lineIndex; index < end; index++ ) { + const parentFormats = formats[ getParentLineIndex( value, startingLineIndex ) ] || []; + const endingLineIndex = getLineIndex( value, end ); + const lastChildIndex = getLastChildIndex( value, endingLineIndex ); + + // Outdent all list items from the starting line index until the last child + // index of the ending list. All children of the ending list need to be + // outdented, otherwise they'll be orphaned. + for ( let index = startingLineIndex; index <= lastChildIndex; index++ ) { + // Skip indices that are not line separators. if ( text[ index ] !== LINE_SEPARATOR ) { continue; } @@ -37,7 +44,7 @@ export function outdentListItems( value ) { ); if ( newFormats[ index ].length === 0 ) { - delete newFormats[ lineIndex ]; + delete newFormats[ index ]; } } diff --git a/packages/rich-text/src/test/get-last-child-index.js b/packages/rich-text/src/test/get-last-child-index.js new file mode 100644 index 00000000000000..55c881d356555c --- /dev/null +++ b/packages/rich-text/src/test/get-last-child-index.js @@ -0,0 +1,50 @@ +/** + * External dependencies + */ +import deepFreeze from 'deep-freeze'; + +/** + * Internal dependencies + */ + +import { getLastChildIndex } from '../get-last-child-index'; +import { LINE_SEPARATOR } from '../special-characters'; + +describe( 'outdentListItems', () => { + const ul = { type: 'ul' }; + + it( 'should return undefined if there is only one line', () => { + expect( getLastChildIndex( deepFreeze( { + formats: [ , ], + text: '1', + } ), undefined ) ).toBe( undefined ); + } ); + + it( 'should return the last line if no line is indented', () => { + expect( getLastChildIndex( deepFreeze( { + formats: [ , ], + text: `1${ LINE_SEPARATOR }`, + } ), undefined ) ).toBe( 1 ); + } ); + + it( 'should return the last child index', () => { + expect( getLastChildIndex( deepFreeze( { + formats: [ , [ ul ], , [ ul ], , ], + text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`, + } ), undefined ) ).toBe( 3 ); + } ); + + it( 'should return the last child index by sibling', () => { + expect( getLastChildIndex( deepFreeze( { + formats: [ , [ ul ], , [ ul ], , ], + text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`, + } ), 1 ) ).toBe( 3 ); + } ); + + it( 'should return the last child index (with further lower indented items)', () => { + expect( getLastChildIndex( deepFreeze( { + formats: [ , [ ul ], , , , ], + text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`, + } ), 1 ) ).toBe( 1 ); + } ); +} ); diff --git a/packages/rich-text/src/test/outdent-list-items.js b/packages/rich-text/src/test/outdent-list-items.js index d321a4c02ffe83..afe1c09d03a314 100644 --- a/packages/rich-text/src/test/outdent-list-items.js +++ b/packages/rich-text/src/test/outdent-list-items.js @@ -21,7 +21,7 @@ describe( 'outdentListItems', () => { start: 1, end: 1, }; - const result = outdentListItems( deepFreeze( record ), ul ); + const result = outdentListItems( deepFreeze( record ) ); expect( result ).toEqual( record ); expect( result ).toBe( record ); @@ -43,7 +43,7 @@ describe( 'outdentListItems', () => { start: 2, end: 2, }; - const result = outdentListItems( deepFreeze( record ), ul ); + const result = outdentListItems( deepFreeze( record ) ); expect( result ).toEqual( expected ); expect( result ).not.toBe( record ); @@ -65,7 +65,7 @@ describe( 'outdentListItems', () => { start: 5, end: 5, }; - const result = outdentListItems( deepFreeze( record ), ul ); + const result = outdentListItems( deepFreeze( record ) ); expect( result ).toEqual( expected ); expect( result ).not.toBe( record ); @@ -87,10 +87,32 @@ describe( 'outdentListItems', () => { start: 2, end: 5, }; - const result = outdentListItems( deepFreeze( record ), ul ); + const result = outdentListItems( deepFreeze( record ) ); expect( result ).toEqual( expected ); expect( result ).not.toBe( record ); expect( getSparseArrayLength( result.formats ) ).toBe( 1 ); } ); + + it( 'should outdent list item with children', () => { + // As we're testing list formats, the text should remain the same. + const text = `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3${ LINE_SEPARATOR }4`; + const record = { + formats: [ , [ ul ], , [ ul, ul ], , [ ul, ul ], , ], + text, + start: 2, + end: 2, + }; + const expected = { + formats: [ , , , [ ul ], , [ ul ], , ], + text, + start: 2, + end: 2, + }; + const result = outdentListItems( deepFreeze( record ) ); + + expect( result ).toEqual( expected ); + expect( result ).not.toBe( record ); + expect( getSparseArrayLength( result.formats ) ).toBe( 2 ); + } ); } ); From bf1c9417d8c92dc37c796aab6be7c0714c47e2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20Van=C2=A0Durpe?= Date: Tue, 29 Jan 2019 17:07:53 +0100 Subject: [PATCH 19/36] RichText: List: Fix getParentIndex (#13562) * RichText: List: Fix getParentIndex * Fill out test name * Add unit tests for getParentLineIndex * Guard against negative lineIndex --- packages/rich-text/src/change-list-type.js | 5 ++- .../rich-text/src/get-parent-line-index.js | 18 +++----- .../src/test/get-parent-line-index.js | 43 +++++++++++++++++++ .../rich-text/src/test/outdent-list-items.js | 22 ++++++++++ 4 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 packages/rich-text/src/test/get-parent-line-index.js diff --git a/packages/rich-text/src/change-list-type.js b/packages/rich-text/src/change-list-type.js index 2b92dda0ab92ce..1dfc0406573636 100644 --- a/packages/rich-text/src/change-list-type.js +++ b/packages/rich-text/src/change-list-type.js @@ -21,9 +21,10 @@ import { getParentLineIndex } from './get-parent-line-index'; */ export function changeListType( value, newFormat ) { const { text, formats, start, end } = value; - const startLineFormats = formats[ getLineIndex( value, start ) ] || []; + const startingLineIndex = getLineIndex( value, start ); + const startLineFormats = formats[ startingLineIndex ] || []; const endLineFormats = formats[ getLineIndex( value, end ) ] || []; - const startIndex = getParentLineIndex( value, start ); + const startIndex = getParentLineIndex( value, startingLineIndex ); const newFormats = formats.slice( 0 ); const startCount = startLineFormats.length - 1; const endCount = endLineFormats.length - 1; diff --git a/packages/rich-text/src/get-parent-line-index.js b/packages/rich-text/src/get-parent-line-index.js index 332764dc45bd30..bd3f72de965196 100644 --- a/packages/rich-text/src/get-parent-line-index.js +++ b/packages/rich-text/src/get-parent-line-index.js @@ -9,27 +9,23 @@ import { LINE_SEPARATOR } from './special-characters'; * go through every list item until we find one with exactly one format type * less. * - * @param {Object} value Value to search. - * @param {number} startIndex Index to start search at. + * @param {Object} value Value to search. + * @param {number} lineIndex Line index of a child list item. * * @return {Array} The parent list line index. */ -export function getParentLineIndex( { text, formats }, startIndex ) { - let index = startIndex; - let startFormats; +export function getParentLineIndex( { text, formats }, lineIndex ) { + const startFormats = formats[ lineIndex ] || []; - while ( index-- ) { + let index = lineIndex; + + while ( index-- >= 0 ) { if ( text[ index ] !== LINE_SEPARATOR ) { continue; } const formatsAtIndex = formats[ index ] || []; - if ( ! startFormats ) { - startFormats = formatsAtIndex; - continue; - } - if ( formatsAtIndex.length === startFormats.length - 1 ) { return index; } diff --git a/packages/rich-text/src/test/get-parent-line-index.js b/packages/rich-text/src/test/get-parent-line-index.js new file mode 100644 index 00000000000000..4e6a75ffd0a6e6 --- /dev/null +++ b/packages/rich-text/src/test/get-parent-line-index.js @@ -0,0 +1,43 @@ +/** + * External dependencies + */ +import deepFreeze from 'deep-freeze'; + +/** + * Internal dependencies + */ + +import { getParentLineIndex } from '../get-parent-line-index'; +import { LINE_SEPARATOR } from '../special-characters'; + +describe( 'getParentLineIndex', () => { + const ul = { type: 'ul' }; + + it( 'should return undefined if there is only one line', () => { + expect( getParentLineIndex( deepFreeze( { + formats: [ , ], + text: '1', + } ), undefined ) ).toBe( undefined ); + } ); + + it( 'should return undefined if the list is part of the first root list child', () => { + expect( getParentLineIndex( deepFreeze( { + formats: [ , ], + text: `1${ LINE_SEPARATOR }2`, + } ), 2 ) ).toBe( undefined ); + } ); + + it( 'should return the line index of the parent list (1)', () => { + expect( getParentLineIndex( deepFreeze( { + formats: [ , , , [ ul ], , ], + text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`, + } ), 3 ) ).toBe( 1 ); + } ); + + it( 'should return the line index of the parent list (2)', () => { + expect( getParentLineIndex( deepFreeze( { + formats: [ , [ ul ], , [ ul, ul ], , [ ul ], , ], + text: `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3${ LINE_SEPARATOR }4`, + } ), 5 ) ).toBe( undefined ); + } ); +} ); diff --git a/packages/rich-text/src/test/outdent-list-items.js b/packages/rich-text/src/test/outdent-list-items.js index afe1c09d03a314..c5b75d5d391886 100644 --- a/packages/rich-text/src/test/outdent-list-items.js +++ b/packages/rich-text/src/test/outdent-list-items.js @@ -115,4 +115,26 @@ describe( 'outdentListItems', () => { expect( result ).not.toBe( record ); expect( getSparseArrayLength( result.formats ) ).toBe( 2 ); } ); + + it( 'should outdent list based on parent list', () => { + // As we're testing list formats, the text should remain the same. + const text = `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3${ LINE_SEPARATOR }4`; + const record = { + formats: [ , [ ul ], , [ ul, ul ], , [ ul ], , ], + text, + start: 6, + end: 6, + }; + const expected = { + formats: [ , [ ul ], , [ ul, ul ], , , , ], + text, + start: 6, + end: 6, + }; + const result = outdentListItems( deepFreeze( record ) ); + + expect( result ).toEqual( expected ); + expect( result ).not.toBe( record ); + expect( getSparseArrayLength( result.formats ) ).toBe( 2 ); + } ); } ); From f84cecae5fdd1488753f5748b6546724e2c40b25 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 29 Jan 2019 11:36:32 -0500 Subject: [PATCH 20/36] Plugin: Populate demo content by default content filters (#13553) * Plugin: Populate demo content by default content filters * Plugin: Avoid checking post status on default content Assumed to be called only in the process of generating a new post for edit (the same assumption checked by testing 'auto-draft' status) * Plugin: Remove unreachable initial_edits code --- gutenberg.php | 13 --------- lib/client-assets.php | 14 +--------- lib/demo.php | 64 +++++++++++++++++++++++++++++++++++++++++++ lib/load.php | 2 +- 4 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 lib/demo.php diff --git a/gutenberg.php b/gutenberg.php index f91df4b9185978..502469ba2f421c 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -254,19 +254,6 @@ function gutenberg_init( $return, $post ) { return true; } -/** - * Redirects the demo page to edit a new post. - */ -function gutenberg_redirect_demo() { - global $pagenow; - - if ( 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'gutenberg' === $_GET['page'] ) { - wp_safe_redirect( admin_url( 'post-new.php?gutenberg-demo' ) ); - exit; - } -} -add_action( 'admin_init', 'gutenberg_redirect_demo' ); - /** * Adds the filters to register additional links for the Gutenberg editor in * the post/page screens. diff --git a/lib/client-assets.php b/lib/client-assets.php index 4fff23ed4bdf5f..634c5352e6e160 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -865,8 +865,6 @@ function gutenberg_get_available_image_sizes() { * @param string $hook Screen name. */ function gutenberg_editor_scripts_and_styles( $hook ) { - $is_demo = isset( $_GET['gutenberg-demo'] ); - global $wp_scripts, $wp_meta_boxes; // Add "wp-hooks" as dependency of "heartbeat". @@ -983,17 +981,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) { // Assign initial edits, if applicable. These are not initially assigned // to the persisted post, but should be included in its save payload. - if ( $is_new_post && $is_demo ) { - // Prepopulate with some test content in demo. - ob_start(); - include gutenberg_dir_path() . 'post-content.php'; - $demo_content = ob_get_clean(); - - $initial_edits = array( - 'title' => __( 'Welcome to the Gutenberg Editor', 'gutenberg' ), - 'content' => $demo_content, - ); - } elseif ( $is_new_post ) { + if ( $is_new_post ) { // Override "(Auto Draft)" new post default title with empty string, // or filtered value. $initial_edits = array( diff --git a/lib/demo.php b/lib/demo.php new file mode 100644 index 00000000000000..11272050bf4c2e --- /dev/null +++ b/lib/demo.php @@ -0,0 +1,64 @@ + Date: Tue, 29 Jan 2019 11:36:51 -0500 Subject: [PATCH 21/36] Plugin: Avoid setting generic "Edit Post" title on load (#13552) --- gutenberg.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index 502469ba2f421c..13f0e148b57de8 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -201,8 +201,6 @@ function gutenberg_pre_init() { * @return bool Whether Gutenberg was initialized. */ function gutenberg_init( $return, $post ) { - global $title, $post_type; - if ( true === $return && current_filter() === 'replace_editor' ) { return $return; } @@ -224,17 +222,6 @@ function gutenberg_init( $return, $post ) { add_filter( 'screen_options_show_screen', '__return_false' ); add_filter( 'admin_body_class', 'gutenberg_add_admin_body_class' ); - $post_type_object = get_post_type_object( $post_type ); - - /* - * Always force to 'Edit Post' (or equivalent) - * because it needs to be in a generic state for both - * post-new.php and post.php?post=<id>. - */ - if ( ! empty( $post_type_object ) ) { - $title = $post_type_object->labels->edit_item; - } - /* * Remove the emoji script as it is incompatible with both React and any * contenteditable fields. From 4165bbd439be60069d59e7935fb57b4523bf2112 Mon Sep 17 00:00:00 2001 From: Andrew Duthie <andrew@andrewduthie.com> Date: Tue, 29 Jan 2019 11:37:38 -0500 Subject: [PATCH 22/36] Plugin: Deprecate window._wpLoadGutenbergEditor (#13547) --- .../backward-compatibility/deprecations.md | 1 + lib/client-assets.php | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/designers-developers/developers/backward-compatibility/deprecations.md b/docs/designers-developers/developers/backward-compatibility/deprecations.md index e4a99cff295136..81cd5352a2cfea 100644 --- a/docs/designers-developers/developers/backward-compatibility/deprecations.md +++ b/docs/designers-developers/developers/backward-compatibility/deprecations.md @@ -56,6 +56,7 @@ The Gutenberg project's deprecation policy is intended to support backward compa - The PHP function `gutenberg_meta_box_post_form_hidden_fields` has been removed. Use [`the_block_editor_meta_box_post_form_hidden_fields`](https://developer.wordpress.org/reference/functions/the_block_editor_meta_box_post_form_hidden_fields/) instead. - The PHP function `gutenberg_toggle_custom_fields` has been removed. - The PHP function `gutenberg_collect_meta_box_data` has been removed. Use [`register_and_do_post_meta_boxes`](https://developer.wordpress.org/reference/functions/register_and_do_post_meta_boxes/) instead. +- `window._wpLoadGutenbergEditor` has been removed. Use `window._wpLoadBlockEditor` instead. Note: This is a private API, not intended for public use. It may be removed in the future. ## 4.5.0 - `Dropdown.refresh()` has been deprecated as the contained `Popover` is now automatically refreshed. diff --git a/lib/client-assets.php b/lib/client-assets.php index 634c5352e6e160..3d5045e3fe67cc 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -1208,11 +1208,27 @@ function gutenberg_editor_scripts_and_styles( $hook ) { $init_script = <<<JS ( function() { - window._wpLoadGutenbergEditor = window._wpLoadBlockEditor = new Promise( function( resolve ) { + window._wpLoadBlockEditor = new Promise( function( resolve ) { wp.domReady( function() { resolve( wp.editPost.initializeEditor( 'editor', "%s", %d, %s, %s ) ); } ); } ); + + Object.defineProperty( window, '_wpLoadGutenbergEditor', { + get: function() { + // TODO: Hello future maintainer. In removing this deprecation, + // ensure also to check whether `wp-editor`'s dependencies in + // `package-dependencies.php` still require `wp-deprecated`. + wp.deprecated( '`window._wpLoadGutenbergEditor`', { + plugin: 'Gutenberg', + version: '5.2', + alternative: '`window._wpLoadBlockEditor`', + hint: 'This is a private API, not intended for public use. It may be removed in the future.' + } ); + + return window._wpLoadBlockEditor; + } + } ); } )(); JS; From d9cb84e5bdc4d4e46884edb83ed8418ab8bc0b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20Van=C2=A0Durpe?= <iseulde@automattic.com> Date: Tue, 29 Jan 2019 18:04:21 +0100 Subject: [PATCH 23/36] RichText: List: fix indentation (#13563) * fix list indentation * Add more tests * Guard against negative lineIndex * Fix outdent error --- packages/rich-text/src/indent-list-items.js | 79 ++++++++++--------- packages/rich-text/src/outdent-list-items.js | 5 +- .../rich-text/src/test/indent-list-items.js | 44 +++++++++++ .../rich-text/src/test/outdent-list-items.js | 22 ++++++ 4 files changed, 113 insertions(+), 37 deletions(-) diff --git a/packages/rich-text/src/indent-list-items.js b/packages/rich-text/src/indent-list-items.js index 39226de4722dc4..516b3c85f96ea2 100644 --- a/packages/rich-text/src/indent-list-items.js +++ b/packages/rich-text/src/indent-list-items.js @@ -6,6 +6,36 @@ import { LINE_SEPARATOR } from './special-characters'; import { normaliseFormats } from './normalise-formats'; import { getLineIndex } from './get-line-index'; +/** + * Gets the line index of the first previous list item with higher indentation. + * + * @param {Object} value Value to search. + * @param {number} lineIndex Line index of the list item to compare with. + * + * @return {boolean} The line index. + */ +function getTargetLevelLineIndex( { text, formats }, lineIndex ) { + const startFormats = formats[ lineIndex ] || []; + + let index = lineIndex; + + while ( index-- >= 0 ) { + if ( text[ index ] !== LINE_SEPARATOR ) { + continue; + } + + const formatsAtIndex = formats[ index ] || []; + + // Return the first line index that is one level higher. If the level is + // lower or equal, there is no result. + if ( formatsAtIndex.length === startFormats.length + 1 ) { + return index; + } else if ( formatsAtIndex.length <= startFormats.length ) { + return; + } + } +} + /** * Indents any selected list items if possible. * @@ -23,62 +53,39 @@ export function indentListItems( value, rootFormat ) { } const { text, formats, start, end } = value; + const previousLineIndex = getLineIndex( value, lineIndex ); const formatsAtLineIndex = formats[ lineIndex ] || []; - const targetFormats = formats[ getLineIndex( value, lineIndex ) ] || []; + const formatsAtPreviousLineIndex = formats[ previousLineIndex ] || []; // The the indentation of the current line is greater than previous line, // then the line cannot be furter indented. - if ( formatsAtLineIndex.length > targetFormats.length ) { + if ( formatsAtLineIndex.length > formatsAtPreviousLineIndex.length ) { return value; } const newFormats = formats.slice(); + const targetLevelLineIndex = getTargetLevelLineIndex( value, lineIndex ); for ( let index = lineIndex; index < end; index++ ) { if ( text[ index ] !== LINE_SEPARATOR ) { continue; } - // If the indentation of the previous line is the same as the current - // line, then duplicate the type and append all current types. E.g. - // - // 1. one - // 2. two <= Selected - // * three <= Selected - // - // should become: - // - // 1. one - // 1. two <= Selected - // * three <= Selected - // - // ^ Inserted list - // - // Otherwise take the target formats and append traling lists. E.g. - // - // 1. one - // * target - // 2. two <= Selected - // * three <= Selected - // - // should become: - // - // 1. one - // * target - // * two <= Selected - // * three <= Selected - // - if ( targetFormats.length === formatsAtLineIndex.length ) { + // Get the previous list, and if there's a child list, take over the + // formats. If not, duplicate the last level and create a new level. + if ( targetLevelLineIndex ) { + const targetFormats = formats[ targetLevelLineIndex ] || []; + newFormats[ index ] = targetFormats.concat( + ( newFormats[ index ] || [] ).slice( targetFormats.length - 1 ) + ); + } else { + const targetFormats = formats[ previousLineIndex ] || []; const lastformat = targetFormats[ targetFormats.length - 1 ] || rootFormat; newFormats[ index ] = targetFormats.concat( [ lastformat ], ( newFormats[ index ] || [] ).slice( targetFormats.length ) ); - } else { - newFormats[ index ] = targetFormats.concat( - ( newFormats[ index ] || [] ).slice( targetFormats.length - 1 ) - ); } } diff --git a/packages/rich-text/src/outdent-list-items.js b/packages/rich-text/src/outdent-list-items.js index b26587f3a0bd13..3a493caa9b03a8 100644 --- a/packages/rich-text/src/outdent-list-items.js +++ b/packages/rich-text/src/outdent-list-items.js @@ -38,9 +38,12 @@ export function outdentListItems( value ) { continue; } + // In the case of level 0, the formats at the index are undefined. + const currentFormats = newFormats[ index ] || []; + // Omit the indentation level where the selection starts. newFormats[ index ] = parentFormats.concat( - newFormats[ index ].slice( parentFormats.length + 1 ) + currentFormats.slice( parentFormats.length + 1 ) ); if ( newFormats[ index ].length === 0 ) { diff --git a/packages/rich-text/src/test/indent-list-items.js b/packages/rich-text/src/test/indent-list-items.js index c55d5063d21a8b..e7f631e5fa8f94 100644 --- a/packages/rich-text/src/test/indent-list-items.js +++ b/packages/rich-text/src/test/indent-list-items.js @@ -130,4 +130,48 @@ describe( 'indentListItems', () => { expect( result ).not.toBe( record ); expect( getSparseArrayLength( result.formats ) ).toBe( 2 ); } ); + + it( 'should indent one level at a time', () => { + // As we're testing list formats, the text should remain the same. + const text = `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3${ LINE_SEPARATOR }4`; + const record = { + formats: [ , [ ul ], , [ ul, ul ], , , , ], + text, + start: 6, + end: 6, + }; + + const result1 = indentListItems( deepFreeze( record ), ul ); + + expect( result1 ).not.toBe( record ); + expect( getSparseArrayLength( result1.formats ) ).toBe( 3 ); + expect( result1 ).toEqual( { + formats: [ , [ ul ], , [ ul, ul ], , [ ul ], , ], + text, + start: 6, + end: 6, + } ); + + const result2 = indentListItems( deepFreeze( result1 ), ul ); + + expect( result2 ).not.toBe( result1 ); + expect( getSparseArrayLength( result2.formats ) ).toBe( 3 ); + expect( result2 ).toEqual( { + formats: [ , [ ul ], , [ ul, ul ], , [ ul, ul ], , ], + text, + start: 6, + end: 6, + } ); + + const result3 = indentListItems( deepFreeze( result2 ), ul ); + + expect( result3 ).not.toBe( result2 ); + expect( getSparseArrayLength( result3.formats ) ).toBe( 3 ); + expect( result3 ).toEqual( { + formats: [ , [ ul ], , [ ul, ul ], , [ ul, ul, ul ], , ], + text, + start: 6, + end: 6, + } ); + } ); } ); diff --git a/packages/rich-text/src/test/outdent-list-items.js b/packages/rich-text/src/test/outdent-list-items.js index c5b75d5d391886..c2bf8b30e4766b 100644 --- a/packages/rich-text/src/test/outdent-list-items.js +++ b/packages/rich-text/src/test/outdent-list-items.js @@ -137,4 +137,26 @@ describe( 'outdentListItems', () => { expect( result ).not.toBe( record ); expect( getSparseArrayLength( result.formats ) ).toBe( 2 ); } ); + + it( 'should outdent when a selected item is at level 0', () => { + // As we're testing list formats, the text should remain the same. + const text = `1${ LINE_SEPARATOR }2${ LINE_SEPARATOR }3`; + const record = { + formats: [ , [ ul ], , , , ], + text, + start: 2, + end: 5, + }; + const expected = { + formats: [ , , , , , ], + text, + start: 2, + end: 5, + }; + const result = outdentListItems( deepFreeze( record ) ); + + expect( result ).toEqual( expected ); + expect( result ).not.toBe( record ); + expect( getSparseArrayLength( result.formats ) ).toBe( 0 ); + } ); } ); From a6f7f9d4dcd80f0a1c8e9b5ae4336afcb4004832 Mon Sep 17 00:00:00 2001 From: Andrew Duthie <andrew@andrewduthie.com> Date: Tue, 29 Jan 2019 12:04:59 -0500 Subject: [PATCH 24/36] Block API: Parse entity only when valid character reference (#13512) * Block API: Rename IdentityEntityParser as DecodeEntityParser * Block API: Parse entity only when valid character reference --- packages/blocks/CHANGELOG.md | 4 ++ packages/blocks/src/api/test/validation.js | 49 +++++++++++-- packages/blocks/src/api/validation.js | 81 ++++++++++++++++++++-- 3 files changed, 123 insertions(+), 11 deletions(-) diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index 4d607751d8a31b..e4a1b5977bcc01 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -4,6 +4,10 @@ - Blocks' `transforms` will receive `innerBlocks` as the second argument (or an array of each block's respective `innerBlocks` for a multi-transform). +### Bug Fixes + +- Block validation will now correctly validate character references, resolving some issues where a standalone ampersand `&` followed later in markup by a character reference (e.g. `&`) could wrongly mark a block as being invalid. ([#13512](https://github.com/WordPress/gutenberg/pull/13512)) + ## 6.0.5 (2019-01-03) ## 6.0.4 (2018-12-12) diff --git a/packages/blocks/src/api/test/validation.js b/packages/blocks/src/api/test/validation.js index babcad7081ae0e..fcfb61bff6ae2c 100644 --- a/packages/blocks/src/api/test/validation.js +++ b/packages/blocks/src/api/test/validation.js @@ -2,7 +2,8 @@ * Internal dependencies */ import { - IdentityEntityParser, + isValidCharacterReference, + DecodeEntityParser, getTextPiecesSplitOnWhitespace, getTextWithCollapsedWhitespace, getMeaningfulAttributePairs, @@ -41,13 +42,39 @@ describe( 'validation', () => { } ); } ); - describe( 'IdentityEntityParser', () => { + describe( 'isValidCharacterReference', () => { + it( 'returns true for a named character reference', () => { + const result = isValidCharacterReference( 'blk12' ); + + expect( result ).toBe( true ); + } ); + + it( 'returns true for a decimal character reference', () => { + const result = isValidCharacterReference( '#33' ); + + expect( result ).toBe( true ); + } ); + + it( 'returns true for a hexadecimal character reference', () => { + const result = isValidCharacterReference( '#xC6' ); + + expect( result ).toBe( true ); + } ); + + it( 'returns false for an invalid character reference', () => { + const result = isValidCharacterReference( ' Test</h2><h2>Test &' ); + + expect( result ).toBe( false ); + } ); + } ); + + describe( 'DecodeEntityParser', () => { it( 'can be constructed', () => { - expect( new IdentityEntityParser() instanceof IdentityEntityParser ).toBe( true ); + expect( new DecodeEntityParser() instanceof DecodeEntityParser ).toBe( true ); } ); it( 'returns parse as decoded value', () => { - expect( new IdentityEntityParser().parse( 'quot' ) ).toBe( '"' ); + expect( new DecodeEntityParser().parse( 'quot' ) ).toBe( '"' ); } ); } ); @@ -397,6 +424,20 @@ describe( 'validation', () => { expect( isEquivalent ).toBe( true ); } ); + it( 'should account for character reference validity', () => { + // Regression: Previously the validator would wrongly evaluate the + // segment of text ` Test</h2><h2>Test &` as a character + // reference, as it's between an opening `&` and terminating `;`. + // + // See: https://github.com/WordPress/gutenberg/issues/12448 + const isEquivalent = isEquivalentHTML( + '<h2>Test & Test</h2><h2>Test & Test</h2>', + '<h2>Test & Test</h2><h2>Test & Test</h2>' + ); + + expect( isEquivalent ).toBe( true ); + } ); + it( 'should return false when more tokens in first', () => { const isEquivalent = isEquivalentHTML( '<div>Hello</div>', diff --git a/packages/blocks/src/api/validation.js b/packages/blocks/src/api/validation.js index 4ccf580b8ffdd9..452c8f832a43fc 100644 --- a/packages/blocks/src/api/validation.js +++ b/packages/blocks/src/api/validation.js @@ -154,24 +154,91 @@ const TEXT_NORMALIZATIONS = [ ]; /** - * Subsitute EntityParser class for `simple-html-tokenizer` which bypasses - * entity substitution in favor of validator's internal normalization. + * Regular expression matching a named character reference. In lieu of bundling + * a full set of references, the pattern covers the minimal necessary to test + * positively against the full set. + * + * "The ampersand must be followed by one of the names given in the named + * character references section, using the same case." + * + * Tested aginst "12.5 Named character references": + * + * ``` + * const references = [ ...document.querySelectorAll( + * '#named-character-references-table tr[id^=entity-] td:first-child' + * ) ].map( ( code ) => code.textContent ) + * references.every( ( reference ) => /^[\da-z]+$/i.test( reference ) ) + * ``` + * + * @link https://html.spec.whatwg.org/multipage/syntax.html#character-references + * @link https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references + * + * @type {RegExp} + */ +const REGEXP_NAMED_CHARACTER_REFERENCE = /^[\da-z]+$/i; + +/** + * Regular expression matching a decimal character reference. + * + * "The ampersand must be followed by a U+0023 NUMBER SIGN character (#), + * followed by one or more ASCII digits, representing a base-ten integer" + * + * @link https://html.spec.whatwg.org/multipage/syntax.html#character-references + * + * @type {RegExp} + */ +const REGEXP_DECIMAL_CHARACTER_REFERENCE = /^#\d+$/; + +/** + * Regular expression matching a hexadecimal character reference. + * + * "The ampersand must be followed by a U+0023 NUMBER SIGN character (#), which + * must be followed by either a U+0078 LATIN SMALL LETTER X character (x) or a + * U+0058 LATIN CAPITAL LETTER X character (X), which must then be followed by + * one or more ASCII hex digits, representing a hexadecimal integer" + * + * @link https://html.spec.whatwg.org/multipage/syntax.html#character-references + * + * @type {RegExp} + */ +const REGEXP_HEXADECIMAL_CHARACTER_REFERENCE = /^#x[\da-f]+$/i; + +/** + * Returns true if the given string is a valid character reference segment, or + * false otherwise. The text should be stripped of `&` and `;` demarcations. + * + * @param {string} text Text to test. + * + * @return {boolean} Whether text is valid character reference. + */ +export function isValidCharacterReference( text ) { + return ( + REGEXP_NAMED_CHARACTER_REFERENCE.test( text ) || + REGEXP_DECIMAL_CHARACTER_REFERENCE.test( text ) || + REGEXP_HEXADECIMAL_CHARACTER_REFERENCE.test( text ) + ); +} + +/** + * Subsitute EntityParser class for `simple-html-tokenizer` which uses the + * implementation of `decodeEntities` from `html-entities`, in order to avoid + * bundling a massive named character reference. * * @see https://github.com/tildeio/simple-html-tokenizer/tree/master/src/entity-parser.ts */ -export class IdentityEntityParser { +export class DecodeEntityParser { /** * Returns a substitute string for an entity string sequence between `&` * and `;`, or undefined if no substitution should occur. * - * In this implementation, undefined is always returned. - * * @param {string} entity Entity fragment discovered in HTML. * * @return {?string} Entity substitute value. */ parse( entity ) { - return decodeEntities( '&' + entity + ';' ); + if ( isValidCharacterReference( entity ) ) { + return decodeEntities( '&' + entity + ';' ); + } } } @@ -451,7 +518,7 @@ export function getNextNonWhitespaceToken( tokens ) { */ function getHTMLTokens( html ) { try { - return new Tokenizer( new IdentityEntityParser() ).tokenize( html ); + return new Tokenizer( new DecodeEntityParser() ).tokenize( html ); } catch ( e ) { log.warning( 'Malformed HTML detected: %s', html ); } From 797df63a80a32d1bfaf5d6b2ed6162e760318c1b Mon Sep 17 00:00:00 2001 From: Andrew Duthie <andrew@andrewduthie.com> Date: Tue, 29 Jan 2019 12:05:14 -0500 Subject: [PATCH 25/36] Plugin: Deprecate gutenberg_get_script_polyfill (#13536) --- .../backward-compatibility/deprecations.md | 1 + lib/client-assets.php | 28 +++------- phpunit/class-polyfill-test.php | 54 ------------------- 3 files changed, 8 insertions(+), 75 deletions(-) delete mode 100644 phpunit/class-polyfill-test.php diff --git a/docs/designers-developers/developers/backward-compatibility/deprecations.md b/docs/designers-developers/developers/backward-compatibility/deprecations.md index 81cd5352a2cfea..6ff124b27a34fa 100644 --- a/docs/designers-developers/developers/backward-compatibility/deprecations.md +++ b/docs/designers-developers/developers/backward-compatibility/deprecations.md @@ -57,6 +57,7 @@ The Gutenberg project's deprecation policy is intended to support backward compa - The PHP function `gutenberg_toggle_custom_fields` has been removed. - The PHP function `gutenberg_collect_meta_box_data` has been removed. Use [`register_and_do_post_meta_boxes`](https://developer.wordpress.org/reference/functions/register_and_do_post_meta_boxes/) instead. - `window._wpLoadGutenbergEditor` has been removed. Use `window._wpLoadBlockEditor` instead. Note: This is a private API, not intended for public use. It may be removed in the future. +- The PHP function `gutenberg_get_script_polyfill` has been removed. Use [`wp_get_script_polyfill`](https://developer.wordpress.org/reference/functions/wp_get_script_polyfill/) instead. ## 4.5.0 - `Dropdown.refresh()` has been deprecated as the contained `Popover` is now automatically refreshed. diff --git a/lib/client-assets.php b/lib/client-assets.php index 3d5045e3fe67cc..469310edbe118c 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -43,27 +43,10 @@ function gutenberg_url( $path ) { * @return string Conditional polyfill inline script. */ function gutenberg_get_script_polyfill( $tests ) { - global $wp_scripts; - - $polyfill = ''; - foreach ( $tests as $test => $handle ) { - if ( ! array_key_exists( $handle, $wp_scripts->registered ) ) { - continue; - } - - $polyfill .= ( - // Test presence of feature... - '( ' . $test . ' ) || ' . - // ...appending polyfill on any failures. Cautious viewers may balk - // at the `document.write`. Its caveat of synchronous mid-stream - // blocking write is exactly the behavior we need though. - 'document.write( \'<script src="' . - esc_url( $wp_scripts->registered[ $handle ]->src ) . - '"></scr\' + \'ipt>\' );' - ); - } + _deprecated_function( __FUNCTION__, '5.0.0', 'wp_get_script_polyfill' ); - return $polyfill; + global $wp_scripts; + return wp_get_script_polyfill( $wp_scripts, $tests ); } if ( ! function_exists( 'register_tinymce_scripts' ) ) { @@ -153,11 +136,14 @@ function gutenberg_register_packages_scripts() { * @since 0.1.0 */ function gutenberg_register_scripts_and_styles() { + global $wp_scripts; + gutenberg_register_vendor_scripts(); wp_add_inline_script( 'wp-polyfill', - gutenberg_get_script_polyfill( + wp_get_script_polyfill( + $wp_scripts, array( '\'fetch\' in window' => 'wp-polyfill-fetch', 'document.contains' => 'wp-polyfill-node-contains', diff --git a/phpunit/class-polyfill-test.php b/phpunit/class-polyfill-test.php deleted file mode 100644 index c64878bd13fb24..00000000000000 --- a/phpunit/class-polyfill-test.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Test gutenberg_get_script_polyfill() - * - * @package Gutenberg - */ - -class Polyfill_Test extends WP_UnitTestCase { - var $old_wp_scripts; - - function setUp() { - parent::setUp(); - $this->old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null; - remove_action( 'wp_default_scripts', 'wp_default_scripts' ); - - $GLOBALS['wp_scripts'] = new WP_Scripts(); - - $GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' ); - - gutenberg_register_scripts_and_styles(); - } - - public function tearDown() { - $GLOBALS['wp_scripts'] = $this->old_wp_scripts; - add_action( 'wp_default_scripts', 'wp_default_scripts' ); - parent::tearDown(); - } - - function test_gutenberg_get_script_polyfill_ignores_missing_handle() { - $polyfill = gutenberg_get_script_polyfill( - array( - '\'Promise\' in window' => 'promise', - ) - ); - - $this->assertEquals( '', $polyfill ); - } - - function test_gutenberg_get_script_polyfill_returns_inline_script() { - wp_register_script( 'promise', 'https://unpkg.com/promise-polyfill/promise.js' ); - - $polyfill = gutenberg_get_script_polyfill( - array( - '\'Promise\' in window' => 'promise', - ) - ); - - $this->assertEquals( - '( \'Promise\' in window ) || document.write( \'<script src="https://unpkg.com/promise-polyfill/promise.js"></scr\' + \'ipt>\' );', - $polyfill - ); - } - -} From e301fc4825df84385ca9b425bcacbe24cbd57a26 Mon Sep 17 00:00:00 2001 From: Riad Benguella <benguella@gmail.com> Date: Mon, 28 Jan 2019 17:57:20 +0100 Subject: [PATCH 26/36] chore(release): publish - @wordpress/annotations@1.0.7 - @wordpress/block-library@2.2.14 - @wordpress/components@7.0.7 - @wordpress/edit-post@3.1.9 - @wordpress/editor@9.0.9 - @wordpress/format-library@1.2.12 - @wordpress/list-reusable-blocks@1.1.20 - @wordpress/nux@3.0.8 - @wordpress/rich-text@3.0.6 --- packages/annotations/package.json | 2 +- packages/block-library/package.json | 2 +- packages/components/package.json | 2 +- packages/edit-post/package.json | 2 +- packages/editor/package.json | 2 +- packages/format-library/package.json | 2 +- packages/list-reusable-blocks/package.json | 2 +- packages/nux/package.json | 2 +- packages/rich-text/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 6a89aa196b2650..34314a94e3f592 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "1.0.6", + "version": "1.0.7", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 03123f6c4aadd3..ad0d614dd2cd2a 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "2.2.13", + "version": "2.2.14", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/package.json b/packages/components/package.json index 2b97622f3da9ce..6d435f2b5ae8c5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "7.0.6", + "version": "7.0.7", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index fc44436ec47922..a062b6a223ffa7 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "3.1.8", + "version": "3.1.9", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/package.json b/packages/editor/package.json index 79684c95217c3f..6b76323a549a4a 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "9.0.8", + "version": "9.0.9", "description": "Building blocks for WordPress editors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/package.json b/packages/format-library/package.json index a18456013c62c4..4394c4eca03f23 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "1.2.11", + "version": "1.2.12", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 9a51dc4dd0957f..36236e8fd53aca 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.19", + "version": "1.1.20", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/nux/package.json b/packages/nux/package.json index b0015ff5c201f9..83268570ffa55a 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/nux", - "version": "3.0.7", + "version": "3.0.8", "description": "NUX (New User eXperience) module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 7c475baec9a525..251217e2e6f496 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "3.0.5", + "version": "3.0.6", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 4a717ae81c1c0637be338ed5cc8b6ce65038e74a Mon Sep 17 00:00:00 2001 From: Riad Benguella <benguella@gmail.com> Date: Tue, 29 Jan 2019 18:16:14 +0100 Subject: [PATCH 27/36] chore(release): publish - @wordpress/annotations@1.0.8 - @wordpress/block-library@2.2.15 - @wordpress/components@7.0.8 - @wordpress/edit-post@3.1.10 - @wordpress/editor@9.0.10 - @wordpress/format-library@1.2.13 - @wordpress/list-reusable-blocks@1.1.21 - @wordpress/nux@3.0.9 - @wordpress/rich-text@3.0.7 --- packages/annotations/package.json | 2 +- packages/block-library/package.json | 2 +- packages/components/package.json | 2 +- packages/edit-post/package.json | 2 +- packages/editor/package.json | 2 +- packages/format-library/package.json | 2 +- packages/list-reusable-blocks/package.json | 2 +- packages/nux/package.json | 2 +- packages/rich-text/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 34314a94e3f592..a259d8a4d55ac4 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "1.0.7", + "version": "1.0.8", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index ad0d614dd2cd2a..855f2257ca3f1d 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "2.2.14", + "version": "2.2.15", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/components/package.json b/packages/components/package.json index 6d435f2b5ae8c5..7cfe766f406176 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "7.0.7", + "version": "7.0.8", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index a062b6a223ffa7..d6ec1e5c769a42 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "3.1.9", + "version": "3.1.10", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/editor/package.json b/packages/editor/package.json index 6b76323a549a4a..f27cff81ed68db 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "9.0.9", + "version": "9.0.10", "description": "Building blocks for WordPress editors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 4394c4eca03f23..cb77cba8519cdf 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "1.2.12", + "version": "1.2.13", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 36236e8fd53aca..06ac0200076863 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.20", + "version": "1.1.21", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/nux/package.json b/packages/nux/package.json index 83268570ffa55a..aadfc11b17e135 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/nux", - "version": "3.0.8", + "version": "3.0.9", "description": "NUX (New User eXperience) module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 251217e2e6f496..541cccc79cc560 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "3.0.6", + "version": "3.0.7", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From b1e26ae9b99c1badeffc52adcdc7c371efd178c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Est=C3=AAv=C3=A3o?= <sergioestevao@gmail.com> Date: Tue, 29 Jan 2019 21:39:44 +0000 Subject: [PATCH 28/36] Rnmobile/media methods refactor (#13554) * Refactor name of media upload methods. * Change method name for media upload sync. * Refactor media request method names on bridge to make clear intent. --- .../block-library/src/image/edit.native.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index eb73ef558542d4..6c5fad9b7b05ec 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -5,10 +5,10 @@ import React from 'react'; import { View, Image, TextInput } from 'react-native'; import { subscribeMediaUpload, - onMediaLibraryPressed, - onUploadMediaPressed, - onCapturePhotoPressed, - onImageQueryReattach, + requestMediaPickFromMediaLibrary, + requestMediaPickFromDeviceLibrary, + requestMediaPickFromDeviceCamera, + mediaUploadSync, } from 'react-native-gutenberg-bridge'; /** @@ -45,7 +45,7 @@ export default class ImageEdit extends React.Component { if ( attributes.id && ! isURL( attributes.url ) ) { this.addMediaUploadListener(); - onImageQueryReattach(); + mediaUploadSync(); } } @@ -108,7 +108,7 @@ export default class ImageEdit extends React.Component { const { url, caption, height, width } = attributes; const onMediaLibraryButtonPressed = () => { - onMediaLibraryPressed( ( mediaId, mediaUrl ) => { + requestMediaPickFromMediaLibrary( ( mediaId, mediaUrl ) => { if ( mediaUrl ) { setAttributes( { id: mediaId, url: mediaUrl } ); } @@ -116,8 +116,8 @@ export default class ImageEdit extends React.Component { }; if ( ! url ) { - const onUploadMediaButtonPressed = () => { - onUploadMediaPressed( ( mediaId, mediaUri ) => { + const onMediaUploadButtonPressed = () => { + requestMediaPickFromDeviceLibrary( ( mediaId, mediaUri ) => { if ( mediaUri ) { this.addMediaUploadListener( ); setAttributes( { url: mediaUri, id: mediaId } ); @@ -125,8 +125,8 @@ export default class ImageEdit extends React.Component { } ); }; - const onCapturePhotoButtonPressed = () => { - onCapturePhotoPressed( ( mediaId, mediaUri ) => { + const onMediaCaptureButtonPressed = () => { + requestMediaPickFromDeviceCamera( ( mediaId, mediaUri ) => { if ( mediaUri ) { this.addMediaUploadListener( ); setAttributes( { url: mediaUri, id: mediaId } ); @@ -136,9 +136,9 @@ export default class ImageEdit extends React.Component { return ( <MediaPlaceholder - onUploadMediaPressed={ onUploadMediaButtonPressed } + onUploadMediaPressed={ onMediaUploadButtonPressed } onMediaLibraryPressed={ onMediaLibraryButtonPressed } - onCapturePhotoPressed={ onCapturePhotoButtonPressed } + onCapturePhotoPressed={ onMediaCaptureButtonPressed } /> ); } From e5e6d6cdadd4d7c2dbc5c3b156387bd0d31cbd28 Mon Sep 17 00:00:00 2001 From: Marcus Kazmierczak <marcus@mkaz.com> Date: Tue, 29 Jan 2019 23:49:46 -0800 Subject: [PATCH 29/36] Docs: Add accessbility specific page (#13169) * Docs: Add accessbility specific page Adds an additional page to the developer handbook on accessibility in particular the landmark regions. Fixes #3217 * Add navigationRegions link --- .../developers/accessibility.md | 17 +++++++++++++++++ docs/manifest.json | 6 ++++++ docs/toc.json | 1 + 3 files changed, 24 insertions(+) create mode 100644 docs/designers-developers/developers/accessibility.md diff --git a/docs/designers-developers/developers/accessibility.md b/docs/designers-developers/developers/accessibility.md new file mode 100644 index 00000000000000..ab6c0d0066b1dc --- /dev/null +++ b/docs/designers-developers/developers/accessibility.md @@ -0,0 +1,17 @@ +# Accessibility + +Accessibility documentation for developers working on the Gutenberg Project. + +For more information on accessibility and WordPress see the [Make WordPress Accessibility Handbook](https://make.wordpress.org/accessibility/handbook/) and the [Accessibility Team section](https://make.wordpress.org/accessibility/). + +## Landmark Regions + +It is a best practice to include ALL content on the page in landmarks, so that screen reader users who rely on them to navigate from section to section do not lose track of content. + +For setting up navigation between different regions, see the [navigateRegions package](/packages/components/src/higher-order/navigate-regions/README.md) for additional documentation. + +Read more regarding landmark design from W3C: + +- [General Principles of Landmark Design](https://www.w3.org/TR/wai-aria-practices-1.1/#general-principles-of-landmark-design) +- [ARIA Landmarks Example](https://www.w3.org/TR/wai-aria-practices/examples/landmarks/) +- [HTML5 elements that by default define ARIA landmarks](https://www.w3.org/TR/wai-aria-practices/examples/landmarks/HTML5.html) diff --git a/docs/manifest.json b/docs/manifest.json index e82a59211c3923..35964c417ab5ec 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -101,6 +101,12 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/internationalization.md", "parent": "developers" }, + { + "title": "Accessibility", + "slug": "accessibility", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/accessibility.md", + "parent": "developers" + }, { "title": "Data Module Reference", "slug": "data", diff --git a/docs/toc.json b/docs/toc.json index cdb3fa096507b6..4e06b65d552ce7 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -18,6 +18,7 @@ {"docs/designers-developers/developers/filters/autocomplete-filters.md": []} ]}, {"docs/designers-developers/developers/internationalization.md": []}, + {"docs/designers-developers/developers/accessibility.md": []}, {"docs/designers-developers/developers/data/README.md": "{{data}}"}, {"docs/designers-developers/developers/packages.md": "{{packages}}"}, {"packages/components/README.md": "{{components}}"}, From d3f9fcf48b7248c9bce22d0bacb46eeb9ad526b7 Mon Sep 17 00:00:00 2001 From: Marcus Kazmierczak <marcus@mkaz.com> Date: Wed, 30 Jan 2019 00:11:30 -0800 Subject: [PATCH 30/36] Update util.js (#13582) Fix typo --- packages/block-library/src/embed/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js index 5352fce1fb58e6..5161d13523c6b6 100644 --- a/packages/block-library/src/embed/util.js +++ b/packages/block-library/src/embed/util.js @@ -51,7 +51,7 @@ export const isFromWordPress = ( html ) => { export const getPhotoHtml = ( photo ) => { // 100% width for the preview so it fits nicely into the document, some "thumbnails" are - // acually the full size photo. + // actually the full size photo. const photoPreview = <p><img src={ photo.thumbnail_url } alt={ photo.title } width="100%" /></p>; return renderToString( photoPreview ); }; From 5105c197279597d1b302822e2d8712c45ad6b3f3 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs <timothy@ironbounddesigns.com> Date: Wed, 30 Jan 2019 05:07:11 -0500 Subject: [PATCH 31/36] Components: Set type=button for TabPanel button elements. (#11944) * Components: Set type=button for TabPanel button elements. The default button type is "submit" which submits the enclosing form. Setting the type property to "button" prevents this behavior. * Use a Button component for Tab buttons * Remove empty line --- packages/components/src/tab-panel/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/components/src/tab-panel/index.js b/packages/components/src/tab-panel/index.js index 2e94bd2b05e14d..5558272f2ffc4a 100644 --- a/packages/components/src/tab-panel/index.js +++ b/packages/components/src/tab-panel/index.js @@ -13,9 +13,10 @@ import { withInstanceId } from '@wordpress/compose'; * Internal dependencies */ import { NavigableMenu } from '../navigable-container'; +import Button from '../button'; const TabButton = ( { tabId, onClick, children, selected, ...rest } ) => ( - <button role="tab" + <Button role="tab" tabIndex={ selected ? null : -1 } aria-selected={ selected } id={ tabId } @@ -23,7 +24,7 @@ const TabButton = ( { tabId, onClick, children, selected, ...rest } ) => ( { ...rest } > { children } - </button> + </Button> ); class TabPanel extends Component { From b5fbda755ce99fdb5e16430d0fb3fcf1f12a298e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= <soerenwrede@gmail.com> Date: Wed, 30 Jan 2019 11:44:35 +0100 Subject: [PATCH 32/36] Add changelog for RSS block (#13588) * Add changelog for RSS block * Update packages/block-library/CHANGELOG.md Co-Authored-By: Soean <soerenwrede@gmail.com> --- packages/block-library/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 398b2af5887d9b..12ab36c1f21047 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -3,6 +3,7 @@ ### New Feature - Add background color controls for the table block. +- Add new `RSS` block ([#7966](https://github.com/WordPress/gutenberg/pull/7966)). ## 2.2.12 (2019-01-03) From b09c855d0dfb47d326a8878fdedbafe4d370dee3 Mon Sep 17 00:00:00 2001 From: Nikolay Ninarski <ninio@shtrak.eu> Date: Wed, 30 Jan 2019 14:21:46 +0200 Subject: [PATCH 33/36] Removed unnecessary className attribute. Fixes #11664 (#11831) ## Description Added a check for empty string to the `onChange` handler of the "Additional CSS Class field". If the string is empty the `setAttributes` is called with `{ className: undefined }` This will resolve #11664 ## How has this been tested? Created a new post and added a heading block. In the Advanced menu, added a class and switched to the Code Editor Tested on local WP running on Windows with Chrome 70.0.3538.102 ## Types of changes Bug fix (non-breaking change which fixes an issue) ## 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/ --> --- packages/editor/src/hooks/custom-class-name.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/hooks/custom-class-name.js b/packages/editor/src/hooks/custom-class-name.js index 9779dfd1a2bb5a..3d4af472276701 100644 --- a/packages/editor/src/hooks/custom-class-name.js +++ b/packages/editor/src/hooks/custom-class-name.js @@ -65,7 +65,7 @@ export const withInspectorControl = createHigherOrderComponent( ( BlockEdit ) => value={ props.attributes.className || '' } onChange={ ( nextValue ) => { props.setAttributes( { - className: nextValue, + className: nextValue !== '' ? nextValue : undefined, } ); } } /> From 62e1522ef5431fcc7f515588b203672d396b7029 Mon Sep 17 00:00:00 2001 From: Andrew Duthie <andrew@andrewduthie.com> Date: Wed, 30 Jan 2019 08:41:14 -0500 Subject: [PATCH 34/36] REST API: Remove oEmbed proxy HTML filtering (#13575) * Plugin: Remove oEmbed proxy HTML filtering * Plugin: Use updated Trac ticket link for oEmbed filter reference * Plugin: Use updated Trac ticket link for oEmbed filter reference --- lib/rest-api.php | 61 ++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/lib/rest-api.php b/lib/rest-api.php index 616493a371219f..d12d7df12dfcc2 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -21,12 +21,9 @@ function gutenberg_register_rest_routes() { } /** - * Make sure oEmbed REST Requests apply the WP Embed security mechanism for WordPress embeds. + * Handle a failing oEmbed proxy request to try embedding as a shortcode. * - * @see https://core.trac.wordpress.org/ticket/32522 - * - * TODO: This is a temporary solution. Next step would be to edit the WP_oEmbed_Controller, - * once merged into Core. + * @see https://core.trac.wordpress.org/ticket/45447 * * @since 2.3.0 * @@ -36,50 +33,32 @@ function gutenberg_register_rest_routes() { * @return WP_HTTP_Response|object|WP_Error The REST Request response. */ function gutenberg_filter_oembed_result( $response, $handler, $request ) { - if ( 'GET' !== $request->get_method() ) { + if ( ! is_wp_error( $response ) || 'oembed_invalid_url' !== $response->get_error_code() || + '/oembed/1.0/proxy' !== $request->get_route() ) { return $response; } - if ( is_wp_error( $response ) && 'oembed_invalid_url' !== $response->get_error_code() ) { + // Try using a classic embed instead. + global $wp_embed; + $html = $wp_embed->shortcode( array(), $_GET['url'] ); + if ( ! $html ) { return $response; } - // External embeds. - if ( '/oembed/1.0/proxy' === $request->get_route() ) { - if ( is_wp_error( $response ) ) { - // It's possibly a local post, so lets try and retrieve it that way. - $post_id = url_to_postid( $_GET['url'] ); - $data = get_oembed_response_data( $post_id, apply_filters( 'oembed_default_width', 600 ) ); - - if ( $data ) { - // It's a local post! - $response = (object) $data; - } else { - // Try using a classic embed, instead. - global $wp_embed; - $html = $wp_embed->shortcode( array(), $_GET['url'] ); - if ( $html ) { - global $wp_scripts; - // Check if any scripts were enqueued by the shortcode, and - // include them in the response. - $enqueued_scripts = array(); - foreach ( $wp_scripts->queue as $script ) { - $enqueued_scripts[] = $wp_scripts->registered[ $script ]->src; - } - return array( - 'provider_name' => __( 'Embed Handler', 'gutenberg' ), - 'html' => $html, - 'scripts' => $enqueued_scripts, - ); - } - } - } - - // Make sure the HTML is run through the oembed sanitisation routines. - $response->html = wp_oembed_get( $_GET['url'], $_GET ); + global $wp_scripts; + + // Check if any scripts were enqueued by the shortcode, and include them in + // the response. + $enqueued_scripts = array(); + foreach ( $wp_scripts->queue as $script ) { + $enqueued_scripts[] = $wp_scripts->registered[ $script ]->src; } - return $response; + return array( + 'provider_name' => __( 'Embed Handler', 'gutenberg' ), + 'html' => $html, + 'scripts' => $enqueued_scripts, + ); } add_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result', 10, 3 ); From 0fa385f13dc306616bf3b24cd1adcb4644df584f Mon Sep 17 00:00:00 2001 From: Sara Cope <sara.cope@gsa.gov> Date: Wed, 30 Jan 2019 09:54:24 -0500 Subject: [PATCH 35/36] Typo fix (#13595) Update the first sentence on line 289 from: "Reducer tests are also be a great fit for snapshots." to: "Reducer tests are also a great fit for snapshots." --- docs/contributors/testing-overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributors/testing-overview.md b/docs/contributors/testing-overview.md index 495109b5f07120..b2a2602491f9d5 100644 --- a/docs/contributors/testing-overview.md +++ b/docs/contributors/testing-overview.md @@ -286,7 +286,7 @@ describe( 'SolarSystem', () => { } ); ``` -Reducer tests are also be a great fit for snapshots. They are often large, complex data structures that shouldn't change unexpectedly, exactly what snapshots excel at! +Reducer tests are also a great fit for snapshots. They are often large, complex data structures that shouldn't change unexpectedly, exactly what snapshots excel at! #### Working with snapshots From 5a8d276eb40ba0defeeff8abf9fee259887dbc44 Mon Sep 17 00:00:00 2001 From: Igor Zinovyev <zinigor@gmail.com> Date: Wed, 30 Jan 2019 18:12:41 +0300 Subject: [PATCH 36/36] Fixes plural messages POT generation. (#13577) * Fixes plural messages POT generation. * Moved the parseInt statement to the assignment. * Added the CHANGELOG.md entry. --- packages/babel-plugin-makepot/CHANGELOG.md | 6 ++++++ packages/babel-plugin-makepot/src/index.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-makepot/CHANGELOG.md b/packages/babel-plugin-makepot/CHANGELOG.md index c70831cbada60c..8079f5197a2fde 100644 --- a/packages/babel-plugin-makepot/CHANGELOG.md +++ b/packages/babel-plugin-makepot/CHANGELOG.md @@ -1,3 +1,9 @@ +## v2.1.1 (Unreleased) + +### Bug Fix + +- Fixed Babel plugin for POT file generation to properly handle plural numbers specified in the passed header. ([#13577](https://github.com/WordPress/gutenberg/pull/13577)) + ## 2.1.0 (2018-09-05) ### New Feature diff --git a/packages/babel-plugin-makepot/src/index.js b/packages/babel-plugin-makepot/src/index.js index 1f9524f524ac57..cf66b8bbc80f3c 100644 --- a/packages/babel-plugin-makepot/src/index.js +++ b/packages/babel-plugin-makepot/src/index.js @@ -246,7 +246,7 @@ module.exports = function() { // Attempt to exract nplurals from header const pluralsMatch = ( baseData.headers[ 'plural-forms' ] || '' ).match( /nplurals\s*=\s*(\d+);/ ); if ( pluralsMatch ) { - nplurals = pluralsMatch[ 1 ]; + nplurals = parseInt( pluralsMatch[ 1 ], 10 ); } }