diff --git a/lib/compat.php b/lib/compat.php new file mode 100644 index 0000000000000..fb09a6e49ef97 --- /dev/null +++ b/lib/compat.php @@ -0,0 +1,26 @@ + { - const { verticalAlignment } = attributes; +/** + * Internal dependencies + */ +import { + toWidthPrecision, + getTotalColumnsWidth, + getColumnWidths, + getAdjacentBlocks, + getRedistributedColumnWidths, +} from '../columns/utils'; + +function ColumnEdit( { + attributes, + updateAlignment, + updateWidth, + hasChildBlocks, +} ) { + const { verticalAlignment, width } = attributes; const classes = classnames( 'block-core-columns', { [ `is-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment, } ); - const onChange = ( alignment ) => updateAlignment( alignment ); - return (
- + + + + + + + ) } + />
); -}; +} export default compose( - withSelect( ( select, { clientId } ) => { - const { getBlockRootClientId } = select( 'core/editor' ); + withSelect( ( select, ownProps ) => { + const { clientId } = ownProps; + const { getBlockOrder } = select( 'core/block-editor' ); return { - parentColumnsBlockClientId: getBlockRootClientId( clientId ), + hasChildBlocks: getBlockOrder( clientId ).length > 0, }; } ), - withDispatch( ( dispatch, { clientId, parentColumnsBlockClientId } ) => { + withDispatch( ( dispatch, ownProps, registry ) => { return { - updateAlignment( alignment ) { - // Update self... - dispatch( 'core/editor' ).updateBlockAttributes( clientId, { - verticalAlignment: alignment, - } ); + updateAlignment( verticalAlignment ) { + const { clientId, setAttributes } = ownProps; + const { updateBlockAttributes } = dispatch( 'core/block-editor' ); + const { getBlockRootClientId } = registry.select( 'core/block-editor' ); + + // Update own alignment. + setAttributes( { verticalAlignment } ); // Reset Parent Columns Block - dispatch( 'core/editor' ).updateBlockAttributes( parentColumnsBlockClientId, { - verticalAlignment: null, + const rootClientId = getBlockRootClientId( clientId ); + updateBlockAttributes( rootClientId, { verticalAlignment: null } ); + }, + updateWidth( width ) { + const { clientId } = ownProps; + const { updateBlockAttributes } = dispatch( 'core/block-editor' ); + const { getBlockRootClientId, getBlocks } = registry.select( 'core/block-editor' ); + + // Constrain or expand siblings to account for gain or loss of + // total columns area. + const columns = getBlocks( getBlockRootClientId( clientId ) ); + const adjacentColumns = getAdjacentBlocks( columns, clientId ); + + // The occupied width is calculated as the sum of the new width + // and the total width of blocks _not_ in the adjacent set. + const occupiedWidth = width + getTotalColumnsWidth( + difference( columns, [ + find( columns, { clientId } ), + ...adjacentColumns, + ] ) + ); + + // Compute _all_ next column widths, in case the updated column + // is in the middle of a set of columns which don't yet have + // any explicit widths assigned (include updates to those not + // part of the adjacent blocks). + const nextColumnWidths = { + ...getColumnWidths( columns, columns.length ), + [ clientId ]: toWidthPrecision( width ), + ...getRedistributedColumnWidths( adjacentColumns, 100 - occupiedWidth, columns.length ), + }; + + forEach( nextColumnWidths, ( nextColumnWidth, columnClientId ) => { + updateBlockAttributes( columnClientId, { width: nextColumnWidth } ); } ); }, }; diff --git a/packages/block-library/src/column/index.js b/packages/block-library/src/column/index.js index 869093459a83b..250ce0bad65a4 100644 --- a/packages/block-library/src/column/index.js +++ b/packages/block-library/src/column/index.js @@ -25,6 +25,16 @@ export const settings = { reusable: false, html: false, }, + getEditWrapperProps( attributes ) { + const { width } = attributes; + if ( Number.isFinite( width ) ) { + return { + style: { + flexBasis: width + '%', + }, + }; + } + }, edit, save, }; diff --git a/packages/block-library/src/column/save.js b/packages/block-library/src/column/save.js index 9e8ea1ac4a3b3..d0dda9de3174b 100644 --- a/packages/block-library/src/column/save.js +++ b/packages/block-library/src/column/save.js @@ -9,13 +9,19 @@ import classnames from 'classnames'; import { InnerBlocks } from '@wordpress/block-editor'; export default function save( { attributes } ) { - const { verticalAlignment } = attributes; + const { verticalAlignment, width } = attributes; + const wrapperClasses = classnames( { [ `is-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment, } ); + let style; + if ( Number.isFinite( width ) ) { + style = { flexBasis: width + '%' }; + } + return ( -
+
); diff --git a/packages/block-library/src/columns/edit.js b/packages/block-library/src/columns/edit.js index 15adfff384dff..7162562523381 100644 --- a/packages/block-library/src/columns/edit.js +++ b/packages/block-library/src/columns/edit.js @@ -2,12 +2,12 @@ * External dependencies */ import classnames from 'classnames'; +import { dropRight } from 'lodash'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { compose } from '@wordpress/compose'; import { PanelBody, RangeControl, @@ -18,12 +18,19 @@ import { BlockControls, BlockVerticalAlignmentToolbar, } from '@wordpress/block-editor'; -import { withSelect, withDispatch } from '@wordpress/data'; +import { withDispatch } from '@wordpress/data'; +import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies */ -import { getColumnsTemplate } from './utils'; +import { + getColumnsTemplate, + hasExplicitColumnWidths, + getMappedColumnWidths, + getRedistributedColumnWidths, + toWidthPrecision, +} from './utils'; /** * Allowed blocks constant is passed to InnerBlocks precisely as specified here. @@ -36,18 +43,18 @@ import { getColumnsTemplate } from './utils'; */ const ALLOWED_BLOCKS = [ 'core/column' ]; -export const ColumnsEdit = function( { attributes, setAttributes, className, updateAlignment } ) { +export function ColumnsEdit( { + attributes, + className, + updateAlignment, + updateColumns, +} ) { const { columns, verticalAlignment } = attributes; const classes = classnames( className, `has-${ columns }-columns`, { [ `are-vertically-aligned-${ verticalAlignment }` ]: verticalAlignment, } ); - const onChange = ( alignment ) => { - // Update all the (immediate) child Column Blocks - updateAlignment( alignment ); - }; - return ( <> @@ -55,11 +62,7 @@ export const ColumnsEdit = function( { attributes, setAttributes, className, upd { - setAttributes( { - columns: nextColumns, - } ); - } } + onChange={ updateColumns } min={ 2 } max={ 6 } /> @@ -67,7 +70,7 @@ export const ColumnsEdit = function( { attributes, setAttributes, className, upd @@ -79,45 +82,81 @@ export const ColumnsEdit = function( { attributes, setAttributes, className, upd
); -}; +} + +export default withDispatch( ( dispatch, ownProps, registry ) => ( { + /** + * Update all child Column blocks with a new vertical alignment setting + * based on whatever alignment is passed in. This allows change to parent + * to overide anything set on a individual column basis. + * + * @param {string} verticalAlignment the vertical alignment setting + */ + updateAlignment( verticalAlignment ) { + const { clientId, setAttributes } = ownProps; + const { updateBlockAttributes } = dispatch( 'core/block-editor' ); + const { getBlockOrder } = registry.select( 'core/block-editor' ); + + // Update own alignment. + setAttributes( { verticalAlignment } ); -const DEFAULT_EMPTY_ARRAY = []; + // Update all child Column Blocks to match + const innerBlockClientIds = getBlockOrder( clientId ); + innerBlockClientIds.forEach( ( innerBlockClientId ) => { + updateBlockAttributes( innerBlockClientId, { + verticalAlignment, + } ); + } ); + }, -export default compose( /** - * Selects the child column Blocks for this parent Column + * Updates the column count, including necessary revisions to child Column + * blocks to grant required or redistribute available space. + * + * @param {number} columns New column count. */ - withSelect( ( select, { clientId } ) => { - const { getBlocksByClientId } = select( 'core/editor' ); - const block = getBlocksByClientId( clientId )[ 0 ]; - - return { - childColumns: block ? block.innerBlocks : DEFAULT_EMPTY_ARRAY, - }; - } ), - withDispatch( ( dispatch, { clientId, childColumns } ) => { - return { - /** - * Update all child column Blocks with a new - * vertical alignment setting based on whatever - * alignment is passed in. This allows change to parent - * to overide anything set on a individual column basis - * - * @param {string} alignment the vertical alignment setting - */ - updateAlignment( alignment ) { - // Update self... - dispatch( 'core/editor' ).updateBlockAttributes( clientId, { - verticalAlignment: alignment, - } ); - - // Update all child Column Blocks to match - childColumns.forEach( ( childColumn ) => { - dispatch( 'core/editor' ).updateBlockAttributes( childColumn.clientId, { - verticalAlignment: alignment, - } ); - } ); - }, - }; - } ), -)( ColumnsEdit ); + updateColumns( columns ) { + const { clientId, setAttributes, attributes } = ownProps; + const { replaceInnerBlocks } = dispatch( 'core/block-editor' ); + const { getBlocks } = registry.select( 'core/block-editor' ); + + // Update columns count. + setAttributes( { columns } ); + + let innerBlocks = getBlocks( clientId ); + if ( ! hasExplicitColumnWidths( innerBlocks ) ) { + return; + } + + // Redistribute available width for existing inner blocks. + const { columns: previousColumns } = attributes; + const isAddingColumn = columns > previousColumns; + + if ( isAddingColumn ) { + // If adding a new column, assign width to the new column equal to + // as if it were `1 / columns` of the total available space. + const newColumnWidth = toWidthPrecision( 100 / columns ); + + // Redistribute in consideration of pending block insertion as + // constraining the available working width. + const widths = getRedistributedColumnWidths( innerBlocks, 100 - newColumnWidth ); + + innerBlocks = [ + ...getMappedColumnWidths( innerBlocks, widths ), + createBlock( 'core/column', { + width: newColumnWidth, + } ), + ]; + } else { + // The removed column will be the last of the inner blocks. + innerBlocks = dropRight( innerBlocks ); + + // Redistribute as if block is already removed. + const widths = getRedistributedColumnWidths( innerBlocks, 100 ); + + innerBlocks = getMappedColumnWidths( innerBlocks, widths ); + } + + replaceInnerBlocks( clientId, innerBlocks, false ); + }, +} ) )( ColumnsEdit ); diff --git a/packages/block-library/src/columns/editor.scss b/packages/block-library/src/columns/editor.scss index 7830f7780880a..c771c79f5f9d3 100644 --- a/packages/block-library/src/columns/editor.scss +++ b/packages/block-library/src/columns/editor.scss @@ -178,14 +178,3 @@ div.block-core-columns.is-vertically-aligned-bottom { display: none; } } - -// In absence of making the individual columns resizable, we prevent them from being clickable. -// This makes them less fiddly. @todo: This should be revisited as the interface is refined. -.wp-block-columns [data-type="core/column"] { - pointer-events: none; - - // This selector re-enables clicking on any child of a column block. - .block-core-columns .block-editor-block-list__layout { - pointer-events: all; - } -} diff --git a/packages/block-library/src/columns/style.scss b/packages/block-library/src/columns/style.scss index ab2ec375ca1e8..7a2c087788933 100644 --- a/packages/block-library/src/columns/style.scss +++ b/packages/block-library/src/columns/style.scss @@ -14,8 +14,11 @@ margin-bottom: 1em; flex-grow: 1; - // Responsiveness: Show at most one columns on mobile. - flex-basis: 100%; + @media (max-width: #{ ($break-small - 1) }) { + // Responsiveness: Show at most one columns on mobile. This must be + // important since the Column assigns its own width as an inline style. + flex-basis: 100% !important; + } // Prevent the columns from growing wider than their distributed sizes. min-width: 0; @@ -30,7 +33,7 @@ flex-basis: calc(50% - #{$grid-size-large}); flex-grow: 0; - // Add space between the 2 columns. Themes can customize this if they wish to work differently. + // Add space between the multiple columns. Themes can customize this if they wish to work differently. // Only apply this beyond the mobile breakpoint, as there's only a single column on mobile. &:nth-child(even) { margin-left: $grid-size-large * 2; diff --git a/packages/block-library/src/columns/test/utils.js b/packages/block-library/src/columns/test/utils.js new file mode 100644 index 0000000000000..cb69e1740e1f3 --- /dev/null +++ b/packages/block-library/src/columns/test/utils.js @@ -0,0 +1,229 @@ +/** + * Internal dependencies + */ +import { + getColumnsTemplate, + toWidthPrecision, + getAdjacentBlocks, + getEffectiveColumnWidth, + getTotalColumnsWidth, + getColumnWidths, + getRedistributedColumnWidths, + hasExplicitColumnWidths, + getMappedColumnWidths, +} from '../utils'; + +describe( 'getColumnsTemplate', () => { + it( 'should return a template corresponding to columns count', () => { + const template = getColumnsTemplate( 4 ); + + expect( template ).toEqual( [ + [ 'core/column' ], + [ 'core/column' ], + [ 'core/column' ], + [ 'core/column' ], + ] ); + } ); +} ); + +describe( 'toWidthPrecision', () => { + it( 'should round value to standard precision', () => { + const value = toWidthPrecision( 50.108 ); + + expect( value ).toBe( 50.11 ); + } ); + + it( 'should return undefined for invalid number', () => { + expect( toWidthPrecision( null ) ).toBe( undefined ); + expect( toWidthPrecision( undefined ) ).toBe( undefined ); + } ); +} ); + +describe( 'getAdjacentBlocks', () => { + const blockA = { clientId: 'a' }; + const blockB = { clientId: 'b' }; + const blockC = { clientId: 'c' }; + const blocks = [ blockA, blockB, blockC ]; + + it( 'should return blocks after clientId', () => { + const result = getAdjacentBlocks( blocks, 'b' ); + + expect( result ).toEqual( [ blockC ] ); + } ); + + it( 'should return blocks before clientId if clientId is last', () => { + const result = getAdjacentBlocks( blocks, 'c' ); + + expect( result ).toEqual( [ blockA, blockB ] ); + } ); +} ); + +describe( 'getEffectiveColumnWidth', () => { + it( 'should return attribute value if set, rounded to precision', () => { + const block = { attributes: { width: 50.108 } }; + + const width = getEffectiveColumnWidth( block, 3 ); + + expect( width ).toBe( 50.11 ); + } ); + + it( 'should return assumed width if attribute value not set, rounded to precision', () => { + const block = { attributes: {} }; + + const width = getEffectiveColumnWidth( block, 3 ); + + expect( width ).toBe( 33.33 ); + } ); +} ); + +describe( 'getTotalColumnsWidth', () => { + describe( 'explicit width', () => { + const blocks = [ + { clientId: 'a', attributes: { width: 30 } }, + { clientId: 'b', attributes: { width: 40 } }, + ]; + + it( 'returns the sum total of columns width', () => { + const width = getTotalColumnsWidth( blocks ); + + expect( width ).toBe( 70 ); + } ); + } ); + + describe( 'implicit width', () => { + const blocks = [ + { clientId: 'a', attributes: {} }, + { clientId: 'b', attributes: {} }, + ]; + + it( 'returns the sum total of columns width', () => { + const widths = getTotalColumnsWidth( blocks ); + + expect( widths ).toBe( 100 ); + } ); + } ); +} ); + +describe( 'getColumnWidths', () => { + describe( 'explicit width', () => { + const blocks = [ + { clientId: 'a', attributes: { width: 30.459 } }, + { clientId: 'b', attributes: { width: 29.543 } }, + ]; + + it( 'returns the column widths', () => { + const widths = getColumnWidths( blocks ); + + expect( widths ).toEqual( { + a: 30.46, + b: 29.54, + } ); + } ); + } ); + + describe( 'implicit width', () => { + const blocks = [ + { clientId: 'a', attributes: {} }, + { clientId: 'b', attributes: {} }, + ]; + + it( 'returns the column widths', () => { + const widths = getColumnWidths( blocks ); + + expect( widths ).toEqual( { + a: 50, + b: 50, + } ); + } ); + } ); +} ); + +describe( 'getRedistributedColumnWidths', () => { + describe( 'explicit width', () => { + const blocks = [ + { clientId: 'a', attributes: { width: 30 } }, + { clientId: 'b', attributes: { width: 40 } }, + ]; + + it( 'should constrain to fit available width', () => { + const widths = getRedistributedColumnWidths( blocks, 60 ); + + expect( widths ).toEqual( { + a: 25, + b: 35, + } ); + } ); + + it( 'should expand to fit available width', () => { + const widths = getRedistributedColumnWidths( blocks, 80 ); + + expect( widths ).toEqual( { + a: 35, + b: 45, + } ); + } ); + } ); + + describe( 'implicit width', () => { + const blocks = [ + { clientId: 'a', attributes: {} }, + { clientId: 'b', attributes: {} }, + ]; + + it( 'should equally distribute to available width', () => { + const widths = getRedistributedColumnWidths( blocks, 60 ); + + expect( widths ).toEqual( { + a: 30, + b: 30, + } ); + } ); + + it( 'should constrain to fit available width', () => { + const widths = getRedistributedColumnWidths( blocks, 66.66, 3 ); + + expect( widths ).toEqual( { + a: 33.33, + b: 33.33, + } ); + } ); + } ); +} ); + +describe( 'hasExplicitColumnWidths', () => { + it( 'returns false if no blocks have explicit width', () => { + const blocks = [ { attributes: {} } ]; + + const result = hasExplicitColumnWidths( blocks ); + + expect( result ).toBe( false ); + } ); + + it( 'returns true if a block has explicit width', () => { + const blocks = [ { attributes: { width: 10 } } ]; + + const result = hasExplicitColumnWidths( blocks ); + + expect( result ).toBe( true ); + } ); +} ); + +describe( 'getMappedColumnWidths', () => { + it( 'merges to block attributes using provided widths', () => { + const blocks = [ + { clientId: 'a', attributes: { width: 30 } }, + { clientId: 'b', attributes: { width: 40 } }, + ]; + const widths = { + a: 25, + b: 35, + }; + + const result = getMappedColumnWidths( blocks, widths ); + + expect( result ).toEqual( [ + { clientId: 'a', attributes: { width: 25 } }, + { clientId: 'b', attributes: { width: 35 } }, + ] ); + } ); +} ); diff --git a/packages/block-library/src/columns/utils.js b/packages/block-library/src/columns/utils.js index e7e3f90df70fd..0c4e4c59e9ccd 100644 --- a/packages/block-library/src/columns/utils.js +++ b/packages/block-library/src/columns/utils.js @@ -2,7 +2,7 @@ * External dependencies */ import memoize from 'memize'; -import { times } from 'lodash'; +import { times, findIndex, sumBy, merge, mapValues } from 'lodash'; /** * Returns the layouts configuration for a given number of columns. @@ -14,3 +14,129 @@ import { times } from 'lodash'; export const getColumnsTemplate = memoize( ( columns ) => { return times( columns, () => [ 'core/column' ] ); } ); + +/** + * Returns a column width attribute value rounded to standard precision. + * Returns `undefined` if the value is not a valid finite number. + * + * @param {?number} value Raw value. + * + * @return {number} Value rounded to standard precision. + */ +export const toWidthPrecision = ( value ) => + Number.isFinite( value ) ? + parseFloat( value.toFixed( 2 ) ) : + undefined; + +/** + * Returns the considered adjacent to that of the specified `clientId` for + * resizing consideration. Adjacent blocks are those occurring after, except + * when the given block is the last block in the set. For the last block, the + * behavior is reversed. + * + * @param {WPBlock[]} blocks Block objects. + * @param {string} clientId Client ID to consider for adjacent blocks. + * + * @return {WPBlock[]} Adjacent block objects. + */ +export function getAdjacentBlocks( blocks, clientId ) { + const index = findIndex( blocks, { clientId } ); + const isLastBlock = index === blocks.length - 1; + + return isLastBlock ? blocks.slice( 0, index ) : blocks.slice( index + 1 ); +} + +/** + * Returns an effective width for a given block. An effective width is equal to + * its attribute value if set, or a computed value assuming equal distribution. + * + * @param {WPBlock} block Block object. + * @param {number} totalBlockCount Total number of blocks in Columns. + * + * @return {number} Effective column width. + */ +export function getEffectiveColumnWidth( block, totalBlockCount ) { + const { width = 100 / totalBlockCount } = block.attributes; + return toWidthPrecision( width ); +} + +/** + * Returns the total width occupied by the given set of column blocks. + * + * @param {WPBlock[]} blocks Block objects. + * @param {?number} totalBlockCount Total number of blocks in Columns. + * Defaults to number of blocks passed. + * + * @return {number} Total width occupied by blocks. + */ +export function getTotalColumnsWidth( blocks, totalBlockCount = blocks.length ) { + return sumBy( blocks, ( block ) => getEffectiveColumnWidth( block, totalBlockCount ) ); +} + +/** + * Returns an object of `clientId` → `width` of effective column widths. + * + * @param {WPBlock[]} blocks Block objects. + * @param {?number} totalBlockCount Total number of blocks in Columns. + * Defaults to number of blocks passed. + * + * @return {Object} Column widths. + */ +export function getColumnWidths( blocks, totalBlockCount = blocks.length ) { + return blocks.reduce( ( result, block ) => { + const width = getEffectiveColumnWidth( block, totalBlockCount ); + return Object.assign( result, { [ block.clientId ]: width } ); + }, {} ); +} + +/** + * Returns an object of `clientId` → `width` of column widths as redistributed + * proportional to their current widths, constrained or expanded to fit within + * the given available width. + * + * @param {WPBlock[]} blocks Block objects. + * @param {number} availableWidth Maximum width to fit within. + * @param {?number} totalBlockCount Total number of blocks in Columns. + * Defaults to number of blocks passed. + * + * @return {Object} Redistributed column widths. + */ +export function getRedistributedColumnWidths( blocks, availableWidth, totalBlockCount = blocks.length ) { + const totalWidth = getTotalColumnsWidth( blocks, totalBlockCount ); + const difference = availableWidth - totalWidth; + const adjustment = difference / blocks.length; + + return mapValues( + getColumnWidths( blocks, totalBlockCount ), + ( width ) => toWidthPrecision( width + adjustment ), + ); +} + +/** + * Returns true if column blocks within the provided set are assigned with + * explicit widths, or false otherwise. + * + * @param {WPBlock[]} blocks Block objects. + * + * @return {boolean} Whether columns have explicit widths. + */ +export function hasExplicitColumnWidths( blocks ) { + return blocks.some( ( block ) => Number.isFinite( block.attributes.width ) ); +} + +/** + * Returns a copy of the given set of blocks with new widths assigned from the + * provided object of redistributed column widths. + * + * @param {WPBlock[]} blocks Block objects. + * @param {Object} widths Redistributed column widths. + * + * @return {WPBlock[]} blocks Mapped block objects. + */ +export function getMappedColumnWidths( blocks, widths ) { + return blocks.map( ( block ) => merge( {}, block, { + attributes: { + width: widths[ block.clientId ], + }, + } ) ); +} diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 97028d2638a19..abde63ef28198 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,7 +1,13 @@ -## 7.4.0 (Unreleased) +## Master + +### New Features - Added a new `HorizontalRule` component. +### Bug Fixes + +- Fixed display of reset button when using RangeControl `allowReset` prop. + ## 7.3.0 (2019-04-16) ### New Features diff --git a/packages/components/src/range-control/index.js b/packages/components/src/range-control/index.js index 2fd28d31bcae1..3b33f2b499c34 100644 --- a/packages/components/src/range-control/index.js +++ b/packages/components/src/range-control/index.js @@ -98,11 +98,17 @@ function RangeControl( { onBlur={ resetCurrentInput } { ...props } /> - { allowReset && - - } + ) } ); } diff --git a/packages/components/src/range-control/style.scss b/packages/components/src/range-control/style.scss index e3dfca023ca83..b67e503592650 100644 --- a/packages/components/src/range-control/style.scss +++ b/packages/components/src/range-control/style.scss @@ -22,6 +22,10 @@ } } +.components-range-control__reset { + margin-left: $grid-size; +} + // creating mixin because we can't do multiline variables, and we can't comma-group the selectors for styling the range slider @mixin range-thumb() { height: 18px; diff --git a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js b/packages/e2e-tests/specs/block-hierarchy-navigation.test.js index 9153526a262a8..77d424d7e6381 100644 --- a/packages/e2e-tests/specs/block-hierarchy-navigation.test.js +++ b/packages/e2e-tests/specs/block-hierarchy-navigation.test.js @@ -22,6 +22,11 @@ describe( 'Navigating the block hierarchy', () => { await insertBlock( 'Columns' ); // Add a paragraph in the first column. + await pressKeyTimes( 'Tab', 5 ); // Tab to inserter. + await page.keyboard.press( 'Enter' ); // Activate inserter. + await page.keyboard.type( 'Paragraph' ); + await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. + await page.keyboard.press( 'Enter' ); // Insert paragraph. await page.keyboard.type( 'First column' ); // Navigate to the columns blocks. @@ -44,7 +49,11 @@ describe( 'Navigating the block hierarchy', () => { await lastColumnsBlockMenuItem.click(); // Insert text in the last column block. - await pressKeyTimes( 'Tab', 5 ); // Navigate to the appender. + await pressKeyTimes( 'Tab', 5 ); // Tab to inserter. + await page.keyboard.press( 'Enter' ); // Activate inserter. + await page.keyboard.type( 'Paragraph' ); + await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. + await page.keyboard.press( 'Enter' ); // Insert paragraph. await page.keyboard.type( 'Third column' ); expect( await getEditedPostContent() ).toMatchSnapshot(); @@ -54,6 +63,11 @@ describe( 'Navigating the block hierarchy', () => { await insertBlock( 'Columns' ); // Add a paragraph in the first column. + await pressKeyTimes( 'Tab', 5 ); // Tab to inserter. + await page.keyboard.press( 'Enter' ); // Activate inserter. + await page.keyboard.type( 'Paragraph' ); + await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. + await page.keyboard.press( 'Enter' ); // Insert paragraph. await page.keyboard.type( 'First column' ); // Navigate to the columns blocks using the keyboard. @@ -76,7 +90,11 @@ describe( 'Navigating the block hierarchy', () => { await page.keyboard.press( 'Enter' ); // Insert text in the last column block - await pressKeyTimes( 'Tab', 5 ); // Navigate to the appender. + await pressKeyTimes( 'Tab', 5 ); // Tab to inserter. + await page.keyboard.press( 'Enter' ); // Activate inserter. + await page.keyboard.type( 'Paragraph' ); + await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. + await page.keyboard.press( 'Enter' ); // Insert paragraph. await page.keyboard.type( 'Third column' ); expect( await getEditedPostContent() ).toMatchSnapshot(); diff --git a/packages/e2e-tests/specs/writing-flow.test.js b/packages/e2e-tests/specs/writing-flow.test.js index c9ca7bf2021df..fe801a3617284 100644 --- a/packages/e2e-tests/specs/writing-flow.test.js +++ b/packages/e2e-tests/specs/writing-flow.test.js @@ -16,6 +16,10 @@ describe( 'adding blocks', () => { } ); it( 'Should navigate inner blocks with arrow keys', async () => { + // TODO: The `waitForSelector` calls in this function should ultimately + // not be necessary for interactions, and exist as a stop-gap solution + // where rendering delays in slower CPU can cause intermittent failure. + let activeElementText; // Add demo content @@ -24,13 +28,22 @@ describe( 'adding blocks', () => { await page.keyboard.press( 'Enter' ); await page.keyboard.type( '/columns' ); await page.keyboard.press( 'Enter' ); + await page.click( ':focus .block-editor-button-block-appender' ); + await page.waitForSelector( ':focus.block-editor-inserter__search' ); + await page.keyboard.type( 'Paragraph' ); + await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. + await page.keyboard.press( 'Enter' ); // Insert paragraph. await page.keyboard.type( 'First col' ); - // Arrow down should navigate through layouts in columns block (to - // its default appender). Two key presses are required since the first - // will land user on the Column wrapper block. - await page.keyboard.press( 'ArrowDown' ); - await page.keyboard.press( 'ArrowDown' ); + // TODO: ArrowDown should traverse into the second column. In slower + // CPUs, it can sometimes remain in the first column paragraph. This + // is a temporary solution. + await page.focus( '.wp-block[data-type="core/column"]:nth-child(2)' ); + await page.click( ':focus .block-editor-button-block-appender' ); + await page.waitForSelector( ':focus.block-editor-inserter__search' ); + await page.keyboard.type( 'Paragraph' ); + await pressKeyTimes( 'Tab', 3 ); // Tab to paragraph result. + await page.keyboard.press( 'Enter' ); // Insert paragraph. await page.keyboard.type( 'Second col' ); // Arrow down from last of layouts exits nested context to default