From 36996032d2ef16e3e15153e93943b80f9cdf7d13 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Wed, 11 May 2022 10:09:04 +1200 Subject: [PATCH 1/7] Move controls to separate file --- packages/block-library/src/cover/controls.js | 381 ++++++++++++++++++ packages/block-library/src/cover/edit.js | 385 ++----------------- packages/block-library/src/cover/shared.js | 4 + 3 files changed, 420 insertions(+), 350 deletions(-) create mode 100644 packages/block-library/src/cover/controls.js diff --git a/packages/block-library/src/cover/controls.js b/packages/block-library/src/cover/controls.js new file mode 100644 index 00000000000000..bc09a18ea9cb59 --- /dev/null +++ b/packages/block-library/src/cover/controls.js @@ -0,0 +1,381 @@ +/** + * WordPress dependencies + */ +import { Fragment, useState, useMemo } from '@wordpress/element'; +import { + BaseControl, + Button, + ExternalLink, + FocalPointPicker, + PanelBody, + PanelRow, + RangeControl, + TextareaControl, + ToggleControl, + ToolbarButton, + __experimentalUseCustomUnits as useCustomUnits, + __experimentalToolsPanelItem as ToolsPanelItem, + __experimentalUnitControl as UnitControl, + __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, +} from '@wordpress/components'; +import { useInstanceId } from '@wordpress/compose'; +import { + BlockControls, + InspectorControls, + MediaReplaceFlow, + useSetting, + __experimentalUseGradient, + __experimentalPanelColorGradientSettings as PanelColorGradientSettings, + __experimentalBlockAlignmentMatrixControl as BlockAlignmentMatrixControl, + __experimentalBlockFullHeightAligmentControl as FullHeightAlignmentControl, +} from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { postFeaturedImage } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { + ALLOWED_MEDIA_TYPES, + COVER_MIN_HEIGHT, + mediaPosition, + IMAGE_BACKGROUND_TYPE, +} from './shared'; + +function CoverHeightInput( { + onChange, + onUnitChange, + unit = 'px', + value = '', +} ) { + const instanceId = useInstanceId( UnitControl ); + const inputId = `block-cover-height-input-${ instanceId }`; + const isPx = unit === 'px'; + + const units = useCustomUnits( { + availableUnits: useSetting( 'spacing.units' ) || [ + 'px', + 'em', + 'rem', + 'vw', + 'vh', + ], + defaultValues: { px: 430, '%': 20, em: 20, rem: 20, vw: 20, vh: 50 }, + } ); + + const handleOnChange = ( unprocessedValue ) => { + const inputValue = + unprocessedValue !== '' + ? parseFloat( unprocessedValue ) + : undefined; + + if ( isNaN( inputValue ) && inputValue !== undefined ) { + return; + } + onChange( inputValue ); + }; + + const computedValue = useMemo( () => { + const [ parsedQuantity ] = parseQuantityAndUnitFromRawValue( value ); + return [ parsedQuantity, unit ].join( '' ); + }, [ unit, value ] ); + + const min = isPx ? COVER_MIN_HEIGHT : 0; + + return ( + + + + ); +} + +export default function Controls( { + attributes, + setAttributes, + clientId, + setOverlayColor, + ref, + onSelectMedia, + currentSettings, +} ) { + const { + contentPosition, + id, + useFeaturedImage, + dimRatio, + focalPoint, + hasParallax, + isRepeated, + minHeight, + minHeightUnit, + alt, + } = attributes; + const { + isVideoBackground, + isImageBackground, + isDarkElement, + hasInnerBlocks, + url, + isImgElement, + overlayColor, + } = currentSettings; + + const [ prevMinHeightValue, setPrevMinHeightValue ] = useState( minHeight ); + const [ prevMinHeightUnit, setPrevMinHeightUnit ] = useState( + minHeightUnit + ); + + const { gradientValue, setGradient } = __experimentalUseGradient(); + const isMinFullHeight = minHeightUnit === 'vh' && minHeight === 100; + const toggleMinFullHeight = () => { + if ( isMinFullHeight ) { + // If there aren't previous values, take the default ones. + if ( prevMinHeightUnit === 'vh' && prevMinHeightValue === 100 ) { + return setAttributes( { + minHeight: undefined, + minHeightUnit: undefined, + } ); + } + + // Set the previous values of height. + return setAttributes( { + minHeight: prevMinHeightValue, + minHeightUnit: prevMinHeightUnit, + } ); + } + + setPrevMinHeightValue( minHeight ); + setPrevMinHeightUnit( minHeightUnit ); + + // Set full height. + return setAttributes( { + minHeight: 100, + minHeightUnit: 'vh', + } ); + }; + + const toggleParallax = () => { + setAttributes( { + hasParallax: ! hasParallax, + ...( ! hasParallax ? { focalPoint: undefined } : {} ), + } ); + }; + + const toggleIsRepeated = () => { + setAttributes( { + isRepeated: ! isRepeated, + } ); + }; + + const toggleUseFeaturedImage = () => { + setAttributes( { + id: undefined, + url: undefined, + useFeaturedImage: ! useFeaturedImage, + dimRatio: dimRatio === 100 ? 50 : dimRatio, + backgroundType: useFeaturedImage + ? IMAGE_BACKGROUND_TYPE + : undefined, + } ); + }; + + const showFocalPointPicker = + isVideoBackground || + ( isImageBackground && ( ! hasParallax || isRepeated ) ); + + const imperativeFocalPointPreview = ( value ) => { + const [ styleOfRef, property ] = isDarkElement.current + ? [ isDarkElement.current.style, 'objectPosition' ] + : [ ref.current.style, 'backgroundPosition' ]; + styleOfRef[ property ] = mediaPosition( value ); + }; + return ( + <> + + + setAttributes( { + contentPosition: nextPosition, + } ) + } + isDisabled={ ! hasInnerBlocks } + /> + + + + + { ! useFeaturedImage && ( + + ) } + + + { !! url && ( + + { isImageBackground && ( + + + + + + ) } + { showFocalPointPicker && ( + + setAttributes( { + focalPoint: newFocalPoint, + } ) + } + /> + ) } + { ! useFeaturedImage && + url && + isImageBackground && + isImgElement && ( + + setAttributes( { alt: newAlt } ) + } + help={ + <> + + { __( + 'Describe the purpose of the image' + ) } + + { __( + 'Leave empty if the image is purely decorative.' + ) } + + } + /> + ) } + + + + + ) } + + + setAttributes( { + dimRatio: newDimRation, + } ) + } + min={ 0 } + max={ 100 } + step={ 10 } + required + /> + + + + !! minHeight } + label={ __( 'Minimum height' ) } + onDeselect={ () => + setAttributes( { + minHeight: undefined, + minHeightUnit: undefined, + } ) + } + resetAllFilter={ () => ( { + minHeight: undefined, + minHeightUnit: undefined, + } ) } + isShownByDefault={ true } + panelId={ clientId } + > + + setAttributes( { minHeight: newMinHeight } ) + } + onUnitChange={ ( nextUnit ) => + setAttributes( { + minHeightUnit: nextUnit, + } ) + } + /> + + + + ); +} diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index a61003e6dc4ff9..7c3fc044c46560 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -9,52 +9,23 @@ import namesPlugin from 'colord/plugins/names'; * WordPress dependencies */ import { useEntityProp, store as coreStore } from '@wordpress/core-data'; +import { useEffect, useRef, useState } from '@wordpress/element'; +import { ResizableBox, Spinner } from '@wordpress/components'; +import { compose } from '@wordpress/compose'; import { - Fragment, - useEffect, - useRef, - useState, - useMemo, -} from '@wordpress/element'; -import { - BaseControl, - Button, - ExternalLink, - FocalPointPicker, - PanelBody, - PanelRow, - RangeControl, - ResizableBox, - Spinner, - TextareaControl, - ToggleControl, - ToolbarButton, - __experimentalUseCustomUnits as useCustomUnits, - __experimentalToolsPanelItem as ToolsPanelItem, - __experimentalUnitControl as UnitControl, - __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, -} from '@wordpress/components'; -import { compose, useInstanceId } from '@wordpress/compose'; -import { - BlockControls, BlockIcon, - InspectorControls, MediaPlaceholder, - MediaReplaceFlow, withColors, ColorPalette, useBlockProps, useSetting, useInnerBlocksProps, __experimentalUseGradient, - __experimentalPanelColorGradientSettings as PanelColorGradientSettings, - __experimentalBlockAlignmentMatrixControl as BlockAlignmentMatrixControl, - __experimentalBlockFullHeightAligmentControl as FullHeightAlignmentControl, store as blockEditorStore, } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; -import { postFeaturedImage, cover as icon } from '@wordpress/icons'; +import { cover as icon } from '@wordpress/icons'; import { isBlobURL } from '@wordpress/blob'; import { store as noticesStore } from '@wordpress/notices'; @@ -66,13 +37,14 @@ import { attributesFromMedia, IMAGE_BACKGROUND_TYPE, VIDEO_BACKGROUND_TYPE, - COVER_MIN_HEIGHT, backgroundImageStyles, dimRatioToClass, isContentPositionCenter, getPositionClassName, + mediaPosition, } from './shared'; import useCoverIsDark from './use-cover-is-dark'; +import Controls from './controls'; extend( [ namesPlugin ] ); @@ -89,62 +61,6 @@ function getInnerBlocksTemplate( attributes ) { ]; } -function CoverHeightInput( { - onChange, - onUnitChange, - unit = 'px', - value = '', -} ) { - const instanceId = useInstanceId( UnitControl ); - const inputId = `block-cover-height-input-${ instanceId }`; - const isPx = unit === 'px'; - - const units = useCustomUnits( { - availableUnits: useSetting( 'spacing.units' ) || [ - 'px', - 'em', - 'rem', - 'vw', - 'vh', - ], - defaultValues: { px: 430, '%': 20, em: 20, rem: 20, vw: 20, vh: 50 }, - } ); - - const handleOnChange = ( unprocessedValue ) => { - const inputValue = - unprocessedValue !== '' - ? parseFloat( unprocessedValue ) - : undefined; - - if ( isNaN( inputValue ) && inputValue !== undefined ) { - return; - } - onChange( inputValue ); - }; - - const computedValue = useMemo( () => { - const [ parsedQuantity ] = parseQuantityAndUnitFromRawValue( value ); - return [ parsedQuantity, unit ].join( '' ); - }, [ unit, value ] ); - - const min = isPx ? COVER_MIN_HEIGHT : 0; - - return ( - - - - ); -} - const RESIZABLE_BOX_ENABLE_OPTION = { top: false, right: false, @@ -190,10 +106,6 @@ function ResizableCover( { ); } -function mediaPosition( { x, y } ) { - return `${ Math.round( x * 100 ) }% ${ Math.round( y * 100 ) }%`; -} - /** * Is the URL a temporary blob URL? A blob URL is one that is used temporarily while * the media (image or video) is being uploaded and will not have an id allocated yet. @@ -287,72 +199,10 @@ function CoverEdit( { blockEditorStore ); const { createErrorNotice } = useDispatch( noticesStore ); - const { - gradientClass, - gradientValue, - setGradient, - } = __experimentalUseGradient(); + const { gradientClass, gradientValue } = __experimentalUseGradient(); const onSelectMedia = attributesFromMedia( setAttributes, dimRatio ); const isUploadingMedia = isTemporaryMedia( id, url ); - const [ prevMinHeightValue, setPrevMinHeightValue ] = useState( minHeight ); - const [ prevMinHeightUnit, setPrevMinHeightUnit ] = useState( - minHeightUnit - ); - const isMinFullHeight = minHeightUnit === 'vh' && minHeight === 100; - - const toggleMinFullHeight = () => { - if ( isMinFullHeight ) { - // If there aren't previous values, take the default ones. - if ( prevMinHeightUnit === 'vh' && prevMinHeightValue === 100 ) { - return setAttributes( { - minHeight: undefined, - minHeightUnit: undefined, - } ); - } - - // Set the previous values of height. - return setAttributes( { - minHeight: prevMinHeightValue, - minHeightUnit: prevMinHeightUnit, - } ); - } - - setPrevMinHeightValue( minHeight ); - setPrevMinHeightUnit( minHeightUnit ); - - // Set full height. - return setAttributes( { - minHeight: 100, - minHeightUnit: 'vh', - } ); - }; - - const toggleParallax = () => { - setAttributes( { - hasParallax: ! hasParallax, - ...( ! hasParallax ? { focalPoint: undefined } : {} ), - } ); - }; - - const toggleIsRepeated = () => { - setAttributes( { - isRepeated: ! isRepeated, - } ); - }; - - const toggleUseFeaturedImage = () => { - setAttributes( { - id: undefined, - url: undefined, - useFeaturedImage: ! useFeaturedImage, - dimRatio: dimRatio === 100 ? 50 : dimRatio, - backgroundType: useFeaturedImage - ? IMAGE_BACKGROUND_TYPE - : undefined, - } ); - }; - const onUploadError = ( message ) => { createErrorNotice( Array.isArray( message ) ? message[ 2 ] : message, { type: 'snackbar', @@ -399,16 +249,6 @@ function CoverEdit( { }; const hasBackground = !! ( url || overlayColor.color || gradientValue ); - const showFocalPointPicker = - isVideoBackground || - ( isImageBackground && ( ! hasParallax || isRepeated ) ); - - const imperativeFocalPointPreview = ( value ) => { - const [ styleOfRef, property ] = isDarkElement.current - ? [ isDarkElement.current.style, 'objectPosition' ] - : [ ref.current.style, 'backgroundPosition' ]; - styleOfRef[ property ] = mediaPosition( value ); - }; const hasInnerBlocks = useSelect( ( select ) => @@ -417,187 +257,6 @@ function CoverEdit( { [ clientId ] ); - const controls = ( - <> - - - setAttributes( { - contentPosition: nextPosition, - } ) - } - isDisabled={ ! hasInnerBlocks } - /> - - - - - { ! useFeaturedImage && ( - - ) } - - - { !! url && ( - - { isImageBackground && ( - - - - - - ) } - { showFocalPointPicker && ( - - setAttributes( { - focalPoint: newFocalPoint, - } ) - } - /> - ) } - { ! useFeaturedImage && - url && - isImageBackground && - isImgElement && ( - - setAttributes( { alt: newAlt } ) - } - help={ - <> - - { __( - 'Describe the purpose of the image' - ) } - - { __( - 'Leave empty if the image is purely decorative.' - ) } - - } - /> - ) } - - - - - ) } - - - setAttributes( { - dimRatio: newDimRation, - } ) - } - min={ 0 } - max={ 100 } - step={ 10 } - required - /> - - - - !! minHeight } - label={ __( 'Minimum height' ) } - onDeselect={ () => - setAttributes( { - minHeight: undefined, - minHeightUnit: undefined, - } ) - } - resetAllFilter={ () => ( { - minHeight: undefined, - minHeightUnit: undefined, - } ) } - isShownByDefault={ true } - panelId={ clientId } - > - - setAttributes( { minHeight: newMinHeight } ) - } - onUnitChange={ ( nextUnit ) => - setAttributes( { - minHeightUnit: nextUnit, - } ) - } - /> - - - - ); - const ref = useRef(); const blockProps = useBlockProps( { ref } ); @@ -619,10 +278,28 @@ function CoverEdit( { } ); + const currentSettings = { + isVideoBackground, + isImageBackground, + isDarkElement, + hasInnerBlocks, + url, + isImgElement, + overlayColor, + }; + if ( ! hasInnerBlocks && ! hasBackground ) { return ( <> - { controls } +
- { controls } +
Date: Thu, 5 May 2022 10:55:23 +1200 Subject: [PATCH 2/7] Clarify naming of isDarkElement --- packages/block-library/src/cover/controls.js | 10 +++++----- packages/block-library/src/cover/edit.js | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/block-library/src/cover/controls.js b/packages/block-library/src/cover/controls.js index bc09a18ea9cb59..7a34c27fc097ea 100644 --- a/packages/block-library/src/cover/controls.js +++ b/packages/block-library/src/cover/controls.js @@ -103,7 +103,7 @@ export default function Controls( { setAttributes, clientId, setOverlayColor, - ref, + coverRef, onSelectMedia, currentSettings, } ) { @@ -122,7 +122,7 @@ export default function Controls( { const { isVideoBackground, isImageBackground, - isDarkElement, + mediaElement, hasInnerBlocks, url, isImgElement, @@ -193,9 +193,9 @@ export default function Controls( { ( isImageBackground && ( ! hasParallax || isRepeated ) ); const imperativeFocalPointPreview = ( value ) => { - const [ styleOfRef, property ] = isDarkElement.current - ? [ isDarkElement.current.style, 'objectPosition' ] - : [ ref.current.style, 'backgroundPosition' ]; + const [ styleOfRef, property ] = mediaElement.current + ? [ mediaElement.current.style, 'objectPosition' ] + : [ coverRef.current.style, 'backgroundPosition' ]; styleOfRef[ property ] = mediaPosition( value ); }; return ( diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 7c3fc044c46560..94151f35fccfe4 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -209,12 +209,12 @@ function CoverEdit( { } ); }; - const isDarkElement = useRef(); + const mediaElement = useRef(); const isCoverDark = useCoverIsDark( url, dimRatio, overlayColor.color, - isDarkElement + mediaElement ); useEffect( () => { @@ -281,7 +281,7 @@ function CoverEdit( { const currentSettings = { isVideoBackground, isImageBackground, - isDarkElement, + mediaElement, hasInnerBlocks, url, isImgElement, @@ -296,7 +296,7 @@ function CoverEdit( { setAttributes={ setAttributes } clientId={ clientId } setOverlayColor={ setOverlayColor } - ref={ ref } + coverRef={ ref } onSelectMedia={ onSelectMedia } currentSettings={ currentSettings } /> @@ -364,7 +364,7 @@ function CoverEdit( { setAttributes={ setAttributes } clientId={ clientId } setOverlayColor={ setOverlayColor } - ref={ ref } + coverRef={ ref } onSelectMedia={ onSelectMedia } currentSettings={ currentSettings } /> @@ -412,7 +412,7 @@ function CoverEdit( { { url && isImageBackground && isImgElement && ( { Date: Thu, 5 May 2022 11:52:42 +1200 Subject: [PATCH 3/7] Split separate components out into their own files --- .../src/cover/cover-placeholder.js | 39 +++++++++ packages/block-library/src/cover/edit.js | 83 +------------------ .../src/cover/resizeable-cover.js | 55 ++++++++++++ 3 files changed, 98 insertions(+), 79 deletions(-) create mode 100644 packages/block-library/src/cover/cover-placeholder.js create mode 100644 packages/block-library/src/cover/resizeable-cover.js diff --git a/packages/block-library/src/cover/cover-placeholder.js b/packages/block-library/src/cover/cover-placeholder.js new file mode 100644 index 00000000000000..aa9f6a565993e8 --- /dev/null +++ b/packages/block-library/src/cover/cover-placeholder.js @@ -0,0 +1,39 @@ +/** + * WordPress dependencies + */ +import { BlockIcon, MediaPlaceholder } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { cover as icon } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { ALLOWED_MEDIA_TYPES } from './shared'; + +export default function CoverPlaceholder( { + disableMediaButtons = false, + children, + onSelectMedia, + onError, + style, +} ) { + return ( + } + labels={ { + title: __( 'Cover' ), + instructions: __( + 'Drag and drop onto this block, upload, or select existing media from your library.' + ), + } } + onSelect={ onSelectMedia } + accept="image/*,video/*" + allowedTypes={ ALLOWED_MEDIA_TYPES } + disableMediaButtons={ disableMediaButtons } + onError={ onError } + style={ style } + > + { children } + + ); +} diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 94151f35fccfe4..2d65a0c1f3fadd 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -9,12 +9,10 @@ import namesPlugin from 'colord/plugins/names'; * WordPress dependencies */ import { useEntityProp, store as coreStore } from '@wordpress/core-data'; -import { useEffect, useRef, useState } from '@wordpress/element'; -import { ResizableBox, Spinner } from '@wordpress/components'; +import { useEffect, useRef } from '@wordpress/element'; +import { Spinner } from '@wordpress/components'; import { compose } from '@wordpress/compose'; import { - BlockIcon, - MediaPlaceholder, withColors, ColorPalette, useBlockProps, @@ -25,7 +23,6 @@ import { } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; -import { cover as icon } from '@wordpress/icons'; import { isBlobURL } from '@wordpress/blob'; import { store as noticesStore } from '@wordpress/notices'; @@ -33,7 +30,6 @@ import { store as noticesStore } from '@wordpress/notices'; * Internal dependencies */ import { - ALLOWED_MEDIA_TYPES, attributesFromMedia, IMAGE_BACKGROUND_TYPE, VIDEO_BACKGROUND_TYPE, @@ -45,6 +41,8 @@ import { } from './shared'; import useCoverIsDark from './use-cover-is-dark'; import Controls from './controls'; +import CoverPlaceholder from './cover-placeholder'; +import ResizableCover from './resizeable-cover'; extend( [ namesPlugin ] ); @@ -61,51 +59,6 @@ function getInnerBlocksTemplate( attributes ) { ]; } -const RESIZABLE_BOX_ENABLE_OPTION = { - top: false, - right: false, - bottom: true, - left: false, - topRight: false, - bottomRight: false, - bottomLeft: false, - topLeft: false, -}; - -function ResizableCover( { - className, - onResizeStart, - onResize, - onResizeStop, - ...props -} ) { - const [ isResizing, setIsResizing ] = useState( false ); - - return ( - { - onResizeStart( elt.clientHeight ); - onResize( elt.clientHeight ); - } } - onResize={ ( _event, _direction, elt ) => { - onResize( elt.clientHeight ); - if ( ! isResizing ) { - setIsResizing( true ); - } - } } - onResizeStop={ ( _event, _direction, elt ) => { - onResizeStop( elt.clientHeight ); - setIsResizing( false ); - } } - { ...props } - /> - ); -} - /** * Is the URL a temporary blob URL? A blob URL is one that is used temporarily while * the media (image or video) is being uploaded and will not have an id allocated yet. @@ -117,34 +70,6 @@ function ResizableCover( { */ const isTemporaryMedia = ( id, url ) => ! id && isBlobURL( url ); -function CoverPlaceholder( { - disableMediaButtons = false, - children, - onSelectMedia, - onError, - style, -} ) { - return ( - } - labels={ { - title: __( 'Cover' ), - instructions: __( - 'Drag and drop onto this block, upload, or select existing media from your library.' - ), - } } - onSelect={ onSelectMedia } - accept="image/*,video/*" - allowedTypes={ ALLOWED_MEDIA_TYPES } - disableMediaButtons={ disableMediaButtons } - onError={ onError } - style={ style } - > - { children } - - ); -} - function CoverEdit( { attributes, clientId, diff --git a/packages/block-library/src/cover/resizeable-cover.js b/packages/block-library/src/cover/resizeable-cover.js new file mode 100644 index 00000000000000..13d5d85395e16c --- /dev/null +++ b/packages/block-library/src/cover/resizeable-cover.js @@ -0,0 +1,55 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { ResizableBox } from '@wordpress/components'; + +const RESIZABLE_BOX_ENABLE_OPTION = { + top: false, + right: false, + bottom: true, + left: false, + topRight: false, + bottomRight: false, + bottomLeft: false, + topLeft: false, +}; + +export default function ResizableCover( { + className, + onResizeStart, + onResize, + onResizeStop, + ...props +} ) { + const [ isResizing, setIsResizing ] = useState( false ); + + return ( + { + onResizeStart( elt.clientHeight ); + onResize( elt.clientHeight ); + } } + onResize={ ( _event, _direction, elt ) => { + onResize( elt.clientHeight ); + if ( ! isResizing ) { + setIsResizing( true ); + } + } } + onResizeStop={ ( _event, _direction, elt ) => { + onResizeStop( elt.clientHeight ); + setIsResizing( false ); + } } + { ...props } + /> + ); +} From 6c1a930f3e1830a71873b3478a51a3b312064a38 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Wed, 11 May 2022 10:13:29 +1200 Subject: [PATCH 4/7] Split up the controls further to allow testing --- .../block-library/src/cover/block-controls.js | 160 ++++++++++++++++++ packages/block-library/src/cover/edit.js | 21 ++- .../{controls.js => inspector-controls.js} | 101 +---------- 3 files changed, 179 insertions(+), 103 deletions(-) create mode 100644 packages/block-library/src/cover/block-controls.js rename packages/block-library/src/cover/{controls.js => inspector-controls.js} (71%) diff --git a/packages/block-library/src/cover/block-controls.js b/packages/block-library/src/cover/block-controls.js new file mode 100644 index 00000000000000..202218278e0a50 --- /dev/null +++ b/packages/block-library/src/cover/block-controls.js @@ -0,0 +1,160 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { ToolbarButton } from '@wordpress/components'; + +import { + BlockControls, + MediaReplaceFlow, + __experimentalBlockAlignmentMatrixControl as BlockAlignmentMatrixControl, + __experimentalBlockFullHeightAligmentControl as FullHeightAlignmentControl, +} from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { postFeaturedImage } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { ALLOWED_MEDIA_TYPES, IMAGE_BACKGROUND_TYPE } from './shared'; + +export function CoverBlockControlsPrimary( { + setAttributes, + contentPosition, + hasInnerBlocks, + minHeightUnit, + minHeight, +} ) { + const [ prevMinHeightValue, setPrevMinHeightValue ] = useState( minHeight ); + const [ prevMinHeightUnit, setPrevMinHeightUnit ] = useState( + minHeightUnit + ); + const isMinFullHeight = minHeightUnit === 'vh' && minHeight === 100; + const toggleMinFullHeight = () => { + if ( isMinFullHeight ) { + // If there aren't previous values, take the default ones. + if ( prevMinHeightUnit === 'vh' && prevMinHeightValue === 100 ) { + return setAttributes( { + minHeight: undefined, + minHeightUnit: undefined, + } ); + } + + // Set the previous values of height. + return setAttributes( { + minHeight: prevMinHeightValue, + minHeightUnit: prevMinHeightUnit, + } ); + } + + setPrevMinHeightValue( minHeight ); + setPrevMinHeightUnit( minHeightUnit ); + + // Set full height. + return setAttributes( { + minHeight: 100, + minHeightUnit: 'vh', + } ); + }; + + return ( + <> + + setAttributes( { + contentPosition: nextPosition, + } ) + } + isDisabled={ ! hasInnerBlocks } + /> + + + ); +} + +export function CoverBlockControlsOther( { + setAttributes, + useFeaturedImage, + dimRatio, + id, + url, + onSelectMedia, +} ) { + const toggleUseFeaturedImage = () => { + setAttributes( { + id: undefined, + url: undefined, + useFeaturedImage: ! useFeaturedImage, + dimRatio: dimRatio === 100 ? 50 : dimRatio, + backgroundType: useFeaturedImage + ? IMAGE_BACKGROUND_TYPE + : undefined, + } ); + }; + return ( + <> + + { ! useFeaturedImage && ( + + ) } + + ); +} +export default function CoverBlockControls( { + attributes, + setAttributes, + onSelectMedia, + currentSettings, +} ) { + const { + contentPosition, + id, + useFeaturedImage, + dimRatio, + minHeight, + minHeightUnit, + } = attributes; + const { hasInnerBlocks, url } = currentSettings; + + return ( + <> + + + + + + + + ); +} diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 2d65a0c1f3fadd..4414d302d6ddd4 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -40,7 +40,8 @@ import { mediaPosition, } from './shared'; import useCoverIsDark from './use-cover-is-dark'; -import Controls from './controls'; +import CoverInspectorControls from './inspector-controls'; +import CoverBlockControls from './block-controls'; import CoverPlaceholder from './cover-placeholder'; import ResizableCover from './resizeable-cover'; @@ -216,13 +217,18 @@ function CoverEdit( { if ( ! hasInnerBlocks && ! hasBackground ) { return ( <> - +
- +
); } - -export default function Controls( { +export default function CoverInspectorControls( { attributes, setAttributes, clientId, setOverlayColor, coverRef, - onSelectMedia, currentSettings, } ) { const { - contentPosition, - id, useFeaturedImage, dimRatio, focalPoint, @@ -123,45 +108,12 @@ export default function Controls( { isVideoBackground, isImageBackground, mediaElement, - hasInnerBlocks, url, isImgElement, overlayColor, } = currentSettings; - const [ prevMinHeightValue, setPrevMinHeightValue ] = useState( minHeight ); - const [ prevMinHeightUnit, setPrevMinHeightUnit ] = useState( - minHeightUnit - ); - const { gradientValue, setGradient } = __experimentalUseGradient(); - const isMinFullHeight = minHeightUnit === 'vh' && minHeight === 100; - const toggleMinFullHeight = () => { - if ( isMinFullHeight ) { - // If there aren't previous values, take the default ones. - if ( prevMinHeightUnit === 'vh' && prevMinHeightValue === 100 ) { - return setAttributes( { - minHeight: undefined, - minHeightUnit: undefined, - } ); - } - - // Set the previous values of height. - return setAttributes( { - minHeight: prevMinHeightValue, - minHeightUnit: prevMinHeightUnit, - } ); - } - - setPrevMinHeightValue( minHeight ); - setPrevMinHeightUnit( minHeightUnit ); - - // Set full height. - return setAttributes( { - minHeight: 100, - minHeightUnit: 'vh', - } ); - }; const toggleParallax = () => { setAttributes( { @@ -176,18 +128,6 @@ export default function Controls( { } ); }; - const toggleUseFeaturedImage = () => { - setAttributes( { - id: undefined, - url: undefined, - useFeaturedImage: ! useFeaturedImage, - dimRatio: dimRatio === 100 ? 50 : dimRatio, - backgroundType: useFeaturedImage - ? IMAGE_BACKGROUND_TYPE - : undefined, - } ); - }; - const showFocalPointPicker = isVideoBackground || ( isImageBackground && ( ! hasParallax || isRepeated ) ); @@ -200,41 +140,6 @@ export default function Controls( { }; return ( <> - - - setAttributes( { - contentPosition: nextPosition, - } ) - } - isDisabled={ ! hasInnerBlocks } - /> - - - - - { ! useFeaturedImage && ( - - ) } - { !! url && ( From 2fb5732810cc34014c1c16e43634102863aa9fbc Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Thu, 5 May 2022 16:22:38 +1200 Subject: [PATCH 5/7] Add a basic block controls test to show how splitting out the controls allows block-level unit tests to be written --- .../src/cover/test/block-controls.js | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 packages/block-library/src/cover/test/block-controls.js diff --git a/packages/block-library/src/cover/test/block-controls.js b/packages/block-library/src/cover/test/block-controls.js new file mode 100644 index 00000000000000..693a74ec2a83eb --- /dev/null +++ b/packages/block-library/src/cover/test/block-controls.js @@ -0,0 +1,50 @@ +/** + * External dependencies + */ +import { render, screen, fireEvent } from '@testing-library/react'; + +/** + * Internal dependencies + */ +import { CoverBlockControlsPrimary } from '../block-controls'; + +const setAttributes = jest.fn(); +const setOverlayColor = jest.fn(); +const onSelectMedia = jest.fn(); + +const defaultProps = { + setAttributes, + setOverlayColor, + onSelectMedia, + hasInnerBlocks: true, + minHeight: 300, + minHeightUnit: 'px', +}; + +beforeEach( () => { + setAttributes.mockClear(); + setOverlayColor.mockClear(); + onSelectMedia.mockClear(); +} ); + +describe( 'Cover block controls', () => { + describe( 'Full height toggle', () => { + test( 'displays toggle full height button toggled off if minHeight not 100vh', () => { + render( ); + expect( + screen.getByRole( 'button', { + pressed: false, + name: 'Toggle full height', + } ) + ).toBeInTheDocument(); + } ); + test( 'sets minHeight attributes to 100vh when clicked', () => { + render( ); + fireEvent.click( screen.getByLabelText( 'Toggle full height' ) ); + expect( setAttributes ).toHaveBeenCalledWith( { + minHeight: 100, + minHeightUnit: 'vh', + } ); + } ); + } ); +} ); From 9d8037ddae3e1cec826e46b57683da21cd9025d1 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Thu, 5 May 2022 16:57:58 +1200 Subject: [PATCH 6/7] Simplify the BlockControls component by mocking the wrapper for testing, rather than unwrapping the children in the component --- .../block-library/src/cover/block-controls.js | 127 ++++++------------ .../src/cover/test/block-controls.js | 30 +++-- 2 files changed, 64 insertions(+), 93 deletions(-) diff --git a/packages/block-library/src/cover/block-controls.js b/packages/block-library/src/cover/block-controls.js index 202218278e0a50..9f265886b42195 100644 --- a/packages/block-library/src/cover/block-controls.js +++ b/packages/block-library/src/cover/block-controls.js @@ -18,13 +18,22 @@ import { postFeaturedImage } from '@wordpress/icons'; */ import { ALLOWED_MEDIA_TYPES, IMAGE_BACKGROUND_TYPE } from './shared'; -export function CoverBlockControlsPrimary( { +export default function CoverBlockControls( { + attributes, setAttributes, - contentPosition, - hasInnerBlocks, - minHeightUnit, - minHeight, + onSelectMedia, + currentSettings, } ) { + const { + contentPosition, + id, + useFeaturedImage, + dimRatio, + minHeight, + minHeightUnit, + } = attributes; + const { hasInnerBlocks, url } = currentSettings; + const [ prevMinHeightValue, setPrevMinHeightValue ] = useState( minHeight ); const [ prevMinHeightUnit, setPrevMinHeightUnit ] = useState( minHeightUnit @@ -57,35 +66,6 @@ export function CoverBlockControlsPrimary( { } ); }; - return ( - <> - - setAttributes( { - contentPosition: nextPosition, - } ) - } - isDisabled={ ! hasInnerBlocks } - /> - - - ); -} - -export function CoverBlockControlsOther( { - setAttributes, - useFeaturedImage, - dimRatio, - id, - url, - onSelectMedia, -} ) { const toggleUseFeaturedImage = () => { setAttributes( { id: undefined, @@ -97,63 +77,42 @@ export function CoverBlockControlsOther( { : undefined, } ); }; - return ( - <> - - { ! useFeaturedImage && ( - - ) } - - ); -} -export default function CoverBlockControls( { - attributes, - setAttributes, - onSelectMedia, - currentSettings, -} ) { - const { - contentPosition, - id, - useFeaturedImage, - dimRatio, - minHeight, - minHeightUnit, - } = attributes; - const { hasInnerBlocks, url } = currentSettings; - return ( <> - + setAttributes( { + contentPosition: nextPosition, + } ) + } + isDisabled={ ! hasInnerBlocks } + /> + - + { ! useFeaturedImage && ( + + ) } ); diff --git a/packages/block-library/src/cover/test/block-controls.js b/packages/block-library/src/cover/test/block-controls.js index 693a74ec2a83eb..25907f9fc7404c 100644 --- a/packages/block-library/src/cover/test/block-controls.js +++ b/packages/block-library/src/cover/test/block-controls.js @@ -3,34 +3,46 @@ */ import { render, screen, fireEvent } from '@testing-library/react'; +// Need to mock the BlockControls wrapper as this requires a slot to run +// so can't be easily unit tested. +jest.mock( '@wordpress/block-editor', () => ( { + ...jest.requireActual( '@wordpress/block-editor' ), + BlockControls: ( { children } ) =>
{ children }
, +} ) ); + /** * Internal dependencies */ -import { CoverBlockControlsPrimary } from '../block-controls'; +import CoverBlockControls from '../block-controls'; const setAttributes = jest.fn(); -const setOverlayColor = jest.fn(); const onSelectMedia = jest.fn(); +const currentSettings = { hasInnerBlocks: true, url: undefined }; +const defaultAttributes = { + contentPosition: undefined, + id: 1, + useFeaturedImage: false, + dimRatio: 50, + minHeight: 300, + minHeightUnit: 'px', +}; const defaultProps = { + attributes: defaultAttributes, + currentSettings, setAttributes, - setOverlayColor, onSelectMedia, - hasInnerBlocks: true, - minHeight: 300, - minHeightUnit: 'px', }; beforeEach( () => { setAttributes.mockClear(); - setOverlayColor.mockClear(); onSelectMedia.mockClear(); } ); describe( 'Cover block controls', () => { describe( 'Full height toggle', () => { test( 'displays toggle full height button toggled off if minHeight not 100vh', () => { - render( ); + render( ); expect( screen.getByRole( 'button', { pressed: false, @@ -39,7 +51,7 @@ describe( 'Cover block controls', () => { ).toBeInTheDocument(); } ); test( 'sets minHeight attributes to 100vh when clicked', () => { - render( ); + render( ); fireEvent.click( screen.getByLabelText( 'Toggle full height' ) ); expect( setAttributes ).toHaveBeenCalledWith( { minHeight: 100, From cf8c83bb41894decddad688f697e2a490d7bc7d9 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Wed, 11 May 2022 10:15:15 +1200 Subject: [PATCH 7/7] Move files into subfolder --- packages/block-library/src/cover/{ => edit}/block-controls.js | 2 +- .../block-library/src/cover/{ => edit}/cover-placeholder.js | 2 +- packages/block-library/src/cover/{edit.js => edit/index.js} | 2 +- .../block-library/src/cover/{ => edit}/inspector-controls.js | 2 +- packages/block-library/src/cover/{ => edit}/resizeable-cover.js | 0 .../block-library/src/cover/{ => edit}/use-cover-is-dark.js | 0 packages/block-library/src/cover/test/block-controls.js | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename packages/block-library/src/cover/{ => edit}/block-controls.js (97%) rename packages/block-library/src/cover/{ => edit}/cover-placeholder.js (94%) rename packages/block-library/src/cover/{edit.js => edit/index.js} (99%) rename packages/block-library/src/cover/{ => edit}/inspector-controls.js (99%) rename packages/block-library/src/cover/{ => edit}/resizeable-cover.js (100%) rename packages/block-library/src/cover/{ => edit}/use-cover-is-dark.js (100%) diff --git a/packages/block-library/src/cover/block-controls.js b/packages/block-library/src/cover/edit/block-controls.js similarity index 97% rename from packages/block-library/src/cover/block-controls.js rename to packages/block-library/src/cover/edit/block-controls.js index 9f265886b42195..aa22ad5f239ec1 100644 --- a/packages/block-library/src/cover/block-controls.js +++ b/packages/block-library/src/cover/edit/block-controls.js @@ -16,7 +16,7 @@ import { postFeaturedImage } from '@wordpress/icons'; /** * Internal dependencies */ -import { ALLOWED_MEDIA_TYPES, IMAGE_BACKGROUND_TYPE } from './shared'; +import { ALLOWED_MEDIA_TYPES, IMAGE_BACKGROUND_TYPE } from '../shared'; export default function CoverBlockControls( { attributes, diff --git a/packages/block-library/src/cover/cover-placeholder.js b/packages/block-library/src/cover/edit/cover-placeholder.js similarity index 94% rename from packages/block-library/src/cover/cover-placeholder.js rename to packages/block-library/src/cover/edit/cover-placeholder.js index aa9f6a565993e8..2f3c070a90f36d 100644 --- a/packages/block-library/src/cover/cover-placeholder.js +++ b/packages/block-library/src/cover/edit/cover-placeholder.js @@ -8,7 +8,7 @@ import { cover as icon } from '@wordpress/icons'; /** * Internal dependencies */ -import { ALLOWED_MEDIA_TYPES } from './shared'; +import { ALLOWED_MEDIA_TYPES } from '../shared'; export default function CoverPlaceholder( { disableMediaButtons = false, diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit/index.js similarity index 99% rename from packages/block-library/src/cover/edit.js rename to packages/block-library/src/cover/edit/index.js index 4414d302d6ddd4..513e222294dbd9 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit/index.js @@ -38,7 +38,7 @@ import { isContentPositionCenter, getPositionClassName, mediaPosition, -} from './shared'; +} from '../shared'; import useCoverIsDark from './use-cover-is-dark'; import CoverInspectorControls from './inspector-controls'; import CoverBlockControls from './block-controls'; diff --git a/packages/block-library/src/cover/inspector-controls.js b/packages/block-library/src/cover/edit/inspector-controls.js similarity index 99% rename from packages/block-library/src/cover/inspector-controls.js rename to packages/block-library/src/cover/edit/inspector-controls.js index e93563669ab17c..b060843c8e6132 100644 --- a/packages/block-library/src/cover/inspector-controls.js +++ b/packages/block-library/src/cover/edit/inspector-controls.js @@ -29,7 +29,7 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { COVER_MIN_HEIGHT, mediaPosition } from './shared'; +import { COVER_MIN_HEIGHT, mediaPosition } from '../shared'; function CoverHeightInput( { onChange, diff --git a/packages/block-library/src/cover/resizeable-cover.js b/packages/block-library/src/cover/edit/resizeable-cover.js similarity index 100% rename from packages/block-library/src/cover/resizeable-cover.js rename to packages/block-library/src/cover/edit/resizeable-cover.js diff --git a/packages/block-library/src/cover/use-cover-is-dark.js b/packages/block-library/src/cover/edit/use-cover-is-dark.js similarity index 100% rename from packages/block-library/src/cover/use-cover-is-dark.js rename to packages/block-library/src/cover/edit/use-cover-is-dark.js diff --git a/packages/block-library/src/cover/test/block-controls.js b/packages/block-library/src/cover/test/block-controls.js index 25907f9fc7404c..ea14dfb38d7b73 100644 --- a/packages/block-library/src/cover/test/block-controls.js +++ b/packages/block-library/src/cover/test/block-controls.js @@ -13,7 +13,7 @@ jest.mock( '@wordpress/block-editor', () => ( { /** * Internal dependencies */ -import CoverBlockControls from '../block-controls'; +import CoverBlockControls from '../edit/block-controls'; const setAttributes = jest.fn(); const onSelectMedia = jest.fn();