diff --git a/packages/block-editor/src/components/child-layout-control/index.js b/packages/block-editor/src/components/child-layout-control/index.js index 78fc483bb7ff07..698ea2d2d74a42 100644 --- a/packages/block-editor/src/components/child-layout-control/index.js +++ b/packages/block-editor/src/components/child-layout-control/index.js @@ -193,8 +193,7 @@ function GridControls( { panelId, } ) { const { columnStart, rowStart, columnSpan, rowSpan } = childLayout; - const { columnCount } = parentLayout ?? {}; - const gridColumnNumber = parseInt( columnCount, 10 ) || 3; + const { columnCount = 3, rowCount } = parentLayout ?? {}; const rootClientId = useSelect( ( select ) => select( blockEditorStore ).getBlockRootClientId( panelId ) ); @@ -202,7 +201,7 @@ function GridControls( { useDispatch( blockEditorStore ); const getNumberOfBlocksBeforeCell = useGetNumberOfBlocksBeforeCell( rootClientId, - gridColumnNumber + columnCount ); const hasStartValue = () => !! columnStart || !! rowStart; const hasSpanValue = () => !! columnSpan || !! rowSpan; @@ -234,14 +233,17 @@ function GridControls( { label={ __( 'Column span' ) } type="number" onChange={ ( value ) => { + // Don't allow unsetting. + const newColumnSpan = + value === '' ? 1 : parseInt( value, 10 ); onChange( { columnStart, rowStart, rowSpan, - columnSpan: value, + columnSpan: newColumnSpan, } ); } } - value={ columnSpan } + value={ columnSpan ?? 1 } min={ 1 } /> { + // Don't allow unsetting. + const newRowSpan = + value === '' ? 1 : parseInt( value, 10 ); onChange( { columnStart, rowStart, columnSpan, - rowSpan: value, + rowSpan: newRowSpan, } ); } } - value={ rowSpan } + value={ rowSpan ?? 1 } min={ 1 } /> @@ -278,8 +283,11 @@ function GridControls( { label={ __( 'Column' ) } type="number" onChange={ ( value ) => { + // Don't allow unsetting. + const newColumnStart = + value === '' ? 1 : parseInt( value, 10 ); onChange( { - columnStart: value, + columnStart: newColumnStart, rowStart, columnSpan, rowSpan, @@ -290,16 +298,16 @@ function GridControls( { rootClientId, rootClientId, getNumberOfBlocksBeforeCell( - value, + newColumnStart, rowStart ) ); } } - value={ columnStart } + value={ columnStart ?? 1 } min={ 1 } max={ - gridColumnNumber - ? gridColumnNumber - ( columnSpan ?? 1 ) + 1 + columnCount + ? columnCount - ( columnSpan ?? 1 ) + 1 : undefined } /> @@ -310,9 +318,12 @@ function GridControls( { label={ __( 'Row' ) } type="number" onChange={ ( value ) => { + // Don't allow unsetting. + const newRowStart = + value === '' ? 1 : parseInt( value, 10 ); onChange( { columnStart, - rowStart: value, + rowStart: newRowStart, columnSpan, rowSpan, } ); @@ -323,17 +334,15 @@ function GridControls( { rootClientId, getNumberOfBlocksBeforeCell( columnStart, - value + newRowStart ) ); } } - value={ rowStart } + value={ rowStart ?? 1 } min={ 1 } max={ - parentLayout?.rowCount - ? parentLayout.rowCount - - ( rowSpan ?? 1 ) + - 1 + rowCount + ? rowCount - ( rowSpan ?? 1 ) + 1 : undefined } /> diff --git a/packages/block-editor/src/components/grid/grid-item-movers.js b/packages/block-editor/src/components/grid/grid-item-movers.js index ed8e35c201f7e0..ddfb7dcb2c2dd5 100644 --- a/packages/block-editor/src/components/grid/grid-item-movers.js +++ b/packages/block-editor/src/components/grid/grid-item-movers.js @@ -32,13 +32,9 @@ export function GridItemMovers( { const columnCount = parentLayout?.columnCount; const rowCount = parentLayout?.rowCount; - const columnCountNumber = parseInt( columnCount, 10 ); - const rowStartNumber = parseInt( rowStart, 10 ); - const columnStartNumber = parseInt( columnStart, 10 ); - const getNumberOfBlocksBeforeCell = useGetNumberOfBlocksBeforeCell( gridClientId, - columnCountNumber + columnCount ); return ( @@ -56,10 +52,7 @@ export function GridItemMovers( { [ blockClientId ], gridClientId, gridClientId, - getNumberOfBlocksBeforeCell( - columnStartNumber, - rowStartNumber - 1 - ) + getNumberOfBlocksBeforeCell( columnStart, rowStart - 1 ) ); } } /> @@ -76,10 +69,7 @@ export function GridItemMovers( { [ blockClientId ], gridClientId, gridClientId, - getNumberOfBlocksBeforeCell( - columnStartNumber, - rowStartNumber + 1 - ) + getNumberOfBlocksBeforeCell( columnStart, rowStart + 1 ) ); } } /> @@ -89,17 +79,14 @@ export function GridItemMovers( { disabled={ columnStart <= 1 } onClick={ () => { onChange( { - columnStart: columnStartNumber - 1, + columnStart: columnStart - 1, } ); __unstableMarkNextChangeAsNotPersistent(); moveBlocksToPosition( [ blockClientId ], gridClientId, gridClientId, - getNumberOfBlocksBeforeCell( - columnStartNumber - 1, - rowStartNumber - ) + getNumberOfBlocksBeforeCell( columnStart - 1, rowStart ) ); } } /> @@ -109,17 +96,14 @@ export function GridItemMovers( { disabled={ columnCount && columnEnd >= columnCount } onClick={ () => { onChange( { - columnStart: columnStartNumber + 1, + columnStart: columnStart + 1, } ); __unstableMarkNextChangeAsNotPersistent(); moveBlocksToPosition( [ blockClientId ], gridClientId, gridClientId, - getNumberOfBlocksBeforeCell( - columnStartNumber + 1, - rowStartNumber - ) + getNumberOfBlocksBeforeCell( columnStart + 1, rowStart ) ); } } /> diff --git a/packages/block-editor/src/hooks/layout-child.js b/packages/block-editor/src/hooks/layout-child.js index 3b77c10b2d18a0..232b44cb9882be 100644 --- a/packages/block-editor/src/hooks/layout-child.js +++ b/packages/block-editor/src/hooks/layout-child.js @@ -35,6 +35,23 @@ function useBlockPropsChildLayoutStyles( { style } ) { const id = useInstanceId( useBlockPropsChildLayoutStyles ); const selector = `.wp-container-content-${ id }`; + // Check that the grid layout attributes are of the correct type, so that we don't accidentally + // write code that stores a string attribute instead of a number. + if ( process.env.NODE_ENV === 'development' ) { + if ( columnStart && typeof columnStart !== 'number' ) { + throw new Error( 'columnStart must be a number' ); + } + if ( rowStart && typeof rowStart !== 'number' ) { + throw new Error( 'rowStart must be a number' ); + } + if ( columnSpan && typeof columnSpan !== 'number' ) { + throw new Error( 'columnSpan must be a number' ); + } + if ( rowSpan && typeof rowSpan !== 'number' ) { + throw new Error( 'rowSpan must be a number' ); + } + } + let css = ''; if ( shouldRenderChildLayoutStyles ) { if ( selfStretch === 'fixed' && flexSize ) { @@ -81,16 +98,6 @@ function useBlockPropsChildLayoutStyles( { style } ) { ( columnSpan || columnStart ) && ( minimumColumnWidth || ! columnCount ) ) { - // Check if columnSpan and columnStart are numbers so Math.max doesn't break. - const columnSpanNumber = columnSpan ? parseInt( columnSpan ) : null; - const columnStartNumber = columnStart - ? parseInt( columnStart ) - : null; - const highestNumber = Math.max( - columnSpanNumber, - columnStartNumber - ); - let parentColumnValue = parseFloat( minimumColumnWidth ); /** * 12rem is the default minimumColumnWidth value. @@ -112,6 +119,7 @@ function useBlockPropsChildLayoutStyles( { style } ) { parentColumnUnit = 'rem'; } + const highestNumber = Math.max( columnSpan, columnStart ); const defaultGapValue = parentColumnUnit === 'px' ? 24 : 1.5; const containerQueryValue = highestNumber * parentColumnValue + diff --git a/packages/block-editor/src/layouts/grid.js b/packages/block-editor/src/layouts/grid.js index 23b8bb5f3909fb..21870fc1820939 100644 --- a/packages/block-editor/src/layouts/grid.js +++ b/packages/block-editor/src/layouts/grid.js @@ -125,6 +125,23 @@ export default { rowCount = null, } = layout; + // Check that the grid layout attributes are of the correct type, so that we don't accidentally + // write code that stores a string attribute instead of a number. + if ( process.env.NODE_ENV === 'development' ) { + if ( + minimumColumnWidth && + typeof minimumColumnWidth !== 'string' + ) { + throw new Error( 'minimumColumnWidth must be a string' ); + } + if ( columnCount && typeof columnCount !== 'number' ) { + throw new Error( 'columnCount must be a number' ); + } + if ( rowCount && typeof rowCount !== 'number' ) { + throw new Error( 'rowCount must be a number' ); + } + } + // If a block's block.json skips serialization for spacing or spacing.blockGap, // don't apply the user-defined value to the styles. const blockGapValue = @@ -241,7 +258,8 @@ function GridLayoutMinimumWidthControl( { layout, onChange } ) { onChange={ ( newValue ) => { onChange( { ...layout, - minimumColumnWidth: newValue, + minimumColumnWidth: + newValue === '' ? undefined : newValue, } ); } } onUnitChange={ handleUnitChange } @@ -274,7 +292,15 @@ function GridLayoutColumnsAndRowsControl( { onChange, allowSizingOnChildren, } ) { - const { columnCount = 3, rowCount, isManualPlacement } = layout; + // If the grid interactivity experiment is enabled, allow unsetting the column count. + const defaultColumnCount = window.__experimentalEnableGridInteractivity + ? undefined + : 3; + const { + columnCount = defaultColumnCount, + rowCount, + isManualPlacement, + } = layout; return ( <> @@ -290,18 +316,31 @@ function GridLayoutColumnsAndRowsControl( { { - /** - * If the input is cleared, avoid switching - * back to "Auto" by setting a value of "1". - */ - const validValue = value !== '' ? value : '1'; - onChange( { - ...layout, - columnCount: - window.__experimentalEnableGridInteractivity - ? parseInt( value, 10 ) || null - : parseInt( validValue, 10 ), - } ); + if ( + window.__experimentalEnableGridInteractivity + ) { + // Allow unsetting the column count when in auto mode. + const defaultNewColumnCount = + isManualPlacement ? 1 : undefined; + const newColumnCount = + value === '' + ? defaultNewColumnCount + : parseInt( value, 10 ); + onChange( { + ...layout, + columnCount: newColumnCount, + } ); + } else { + // Don't allow unsetting the column count. + const newColumnCount = + value === '' + ? 1 + : parseInt( value, 10 ); + onChange( { + ...layout, + columnCount: newColumnCount, + } ); + } } } value={ columnCount } min={ 0 } @@ -320,9 +359,14 @@ function GridLayoutColumnsAndRowsControl( { { + // Don't allow unsetting the row count. + const newRowCount = + value === '' + ? 1 + : parseInt( value, 10 ); onChange( { ...layout, - rowCount: parseInt( value, 10 ), + rowCount: newRowCount, } ); } } value={ rowCount } @@ -331,7 +375,7 @@ function GridLayoutColumnsAndRowsControl( { /> ) : ( onChange( { ...layout, diff --git a/packages/blocks/src/api/parser/convert-legacy-block.js b/packages/blocks/src/api/parser/convert-legacy-block.js index 055679302efd64..1ddec1d8b3448d 100644 --- a/packages/blocks/src/api/parser/convert-legacy-block.js +++ b/packages/blocks/src/api/parser/convert-legacy-block.js @@ -77,6 +77,44 @@ export function convertLegacyBlockNameAndAttributes( name, attributes ) { newAttributes.legacy = true; } + // Column count was stored as a string from WP 6.3-6.6. Convert it to a number. + if ( + attributes.layout?.type === 'grid' && + typeof attributes.layout?.columnCount === 'string' + ) { + newAttributes.layout = { + ...newAttributes.layout, + columnCount: parseInt( attributes.layout.columnCount, 10 ), + }; + } + + // Column span and row span were stored as strings in WP 6.6. Convert them to numbers. + if ( typeof attributes.style?.layout?.columnSpan === 'string' ) { + const columnSpanNumber = parseInt( + attributes.style.layout.columnSpan, + 10 + ); + newAttributes.style = { + ...newAttributes.style, + layout: { + ...newAttributes.style.layout, + columnSpan: isNaN( columnSpanNumber ) + ? undefined + : columnSpanNumber, + }, + }; + } + if ( typeof attributes.style?.layout?.rowSpan === 'string' ) { + const rowSpanNumber = parseInt( attributes.style.layout.rowSpan, 10 ); + newAttributes.style = { + ...newAttributes.style, + layout: { + ...newAttributes.style.layout, + rowSpan: isNaN( rowSpanNumber ) ? undefined : rowSpanNumber, + }, + }; + } + // The following code is only relevant for the Gutenberg plugin. // It's a stand-alone if statement for dead-code elimination. if ( globalThis.IS_GUTENBERG_PLUGIN ) {