From 5b23becc305ddc83217feae20360e79d0a6c2b81 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Fri, 14 Oct 2022 08:52:18 +0300 Subject: [PATCH 1/9] [Block Library - Image]: Add toolbar button to add a caption --- packages/block-library/src/image/image.js | 85 +++++++++++++++-------- packages/icons/src/index.js | 1 + packages/icons/src/library/caption.js | 16 +++++ 3 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 packages/icons/src/library/caption.js diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index ff0b0dddde561..ee359b7484605 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -40,7 +40,12 @@ import { getDefaultBlockName, switchToBlockType, } from '@wordpress/blocks'; -import { crop, overlayText, upload } from '@wordpress/icons'; +import { + crop, + overlayText, + upload, + caption as captionIcon, +} from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; @@ -89,7 +94,8 @@ export default function Image( { } = attributes; const imageRef = useRef(); const captionRef = useRef(); - const prevUrl = usePrevious( url ); + const prevCaption = usePrevious( caption ); + const [ showCaption, setShowCaption ] = useState( !! caption ); const { allowResize = true } = context; const { getBlock } = useSelect( blockEditorStore ); @@ -180,15 +186,20 @@ export default function Image( { .catch( () => {} ); }, [ id, url, isSelected, externalBlob ] ); - // Focus the caption after inserting an image from the placeholder. This is - // done to preserve the behaviour of focussing the first tabbable element - // when a block is mounted. Previously, the image block would remount when - // the placeholder is removed. Maybe this behaviour could be removed. + // We need to show the caption when changes come from + // history navigation(undo/redo). + useEffect( () => { + if ( caption && ! prevCaption ) { + setShowCaption( true ); + } + }, [ caption, prevCaption ] ); + + // Focus the caption when we click to add one. useEffect( () => { - if ( url && ! prevUrl && isSelected ) { - captionRef.current.focus(); + if ( showCaption && ! caption ) { + captionRef.current?.focus(); } - }, [ url, prevUrl ] ); + }, [ caption, showCaption ] ); // Get naturalWidth and naturalHeight from image ref, and fall back to loaded natural // width and height. This resolves an issue in Safari where the loaded natural @@ -297,8 +308,11 @@ export default function Image( { useEffect( () => { if ( ! isSelected ) { setIsEditingImage( false ); + if ( ! caption ) { + setShowCaption( false ); + } } - }, [ isSelected ] ); + }, [ isSelected, caption ] ); const canEditImage = id && naturalWidth && naturalHeight && imageEditing; const allowCrop = ! multiImageSelection && canEditImage && ! isEditingImage; @@ -319,6 +333,16 @@ export default function Image( { onChange={ updateAlignment } /> ) } + { ! isContentLocked && ! caption && ( + { + setShowCaption( ! showCaption ); + } } + icon={ captionIcon } + disabled={ showCaption } + label={ __( 'Add caption' ) } + /> + ) } { ! multiImageSelection && ! isEditingImage && ( - setAttributes( { caption: value } ) - } - inlineToolbar - __unstableOnSplitAtEnd={ () => - insertBlocksAfter( - createBlock( getDefaultBlockName() ) - ) - } - /> - ) } + { showCaption && + ( ! RichText.isEmpty( caption ) || isSelected ) && ( + + setAttributes( { caption: value } ) + } + inlineToolbar + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( + createBlock( getDefaultBlockName() ) + ) + } + /> + ) } ); } diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js index bfc32a594d33c..f5071f80d1a1e 100644 --- a/packages/icons/src/index.js +++ b/packages/icons/src/index.js @@ -27,6 +27,7 @@ export { default as button } from './library/button'; export { default as buttons } from './library/buttons'; export { default as calendar } from './library/calendar'; export { default as cancelCircleFilled } from './library/cancel-circle-filled'; +export { default as caption } from './library/caption'; export { default as capturePhoto } from './library/capture-photo'; export { default as captureVideo } from './library/capture-video'; export { default as category } from './library/category'; diff --git a/packages/icons/src/library/caption.js b/packages/icons/src/library/caption.js new file mode 100644 index 0000000000000..b6fd950c31b51 --- /dev/null +++ b/packages/icons/src/library/caption.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { Path, SVG } from '@wordpress/primitives'; + +const caption = ( + + + +); + +export default caption; From 99ac960e5c7ae368564713b4fbd04bb9f2ff5471 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Fri, 14 Oct 2022 12:09:20 +0300 Subject: [PATCH 2/9] update tests --- .../e2e-tests/specs/editor/blocks/gallery.test.js | 3 ++- test/e2e/specs/editor/blocks/image.spec.js | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/e2e-tests/specs/editor/blocks/gallery.test.js b/packages/e2e-tests/specs/editor/blocks/gallery.test.js index 8ffe087f2668a..79bd8a3441d1b 100644 --- a/packages/e2e-tests/specs/editor/blocks/gallery.test.js +++ b/packages/e2e-tests/specs/editor/blocks/gallery.test.js @@ -16,6 +16,7 @@ import { clickButton, openListView, getListViewBlocks, + clickBlockToolbarButton, } from '@wordpress/e2e-test-utils'; async function upload( selector ) { @@ -110,7 +111,7 @@ describe( 'Gallery', () => { const imageListLink = ( await getListViewBlocks( 'Image' ) )[ 0 ]; await imageListLink.click(); - + await clickBlockToolbarButton( 'Add caption' ); const captionElement = await figureElement.$( '.block-editor-rich-text__editable' ); diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index a86f9c1d20443..209511d2d1cfa 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -141,7 +141,7 @@ test.describe( 'Image', () => { } } ); - test( 'should place caret at end of caption after merging empty paragraph', async ( { + test( 'should place caret on caption when clicking to add one', async ( { editor, page, imageBlockUtils, @@ -157,7 +157,7 @@ test.describe( 'Image', () => { imageBlock.locator( 'data-testid=form-file-upload-input' ) ); await expect( image ).toHaveAttribute( 'src', new RegExp( filename ) ); - + await editor.clickBlockToolbarButton( 'Add caption' ); await page.keyboard.type( '1' ); await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'Backspace' ); @@ -186,7 +186,7 @@ test.describe( 'Image', () => { await expect( image ).toBeVisible(); await expect( image ).toHaveAttribute( 'src', new RegExp( fileName ) ); - + await editor.clickBlockToolbarButton( 'Add caption' ); await page.keyboard.type( '12' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.press( 'Enter' ); @@ -216,7 +216,8 @@ test.describe( 'Image', () => { await expect( image ).toBeVisible(); await expect( image ).toHaveAttribute( 'src', new RegExp( fileName ) ); - // Navigate to inline toolbar, + // Add caption and navigate to inline toolbar. + await editor.clickBlockToolbarButton( 'Add caption' ); await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); await expect( await page.evaluate( () => @@ -516,7 +517,7 @@ test.describe( 'Image', () => { ); await expect( image ).toHaveAttribute( 'src', new RegExp( filename ) ); - + await page.focus( '.wp-block-image' ); await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Expect an empty image block (placeholder) rather than one with a From 26bc81b855281198a2e9e080a4551cc00e812fc8 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Fri, 14 Oct 2022 12:42:20 +0300 Subject: [PATCH 3/9] change resizable box container css.display --- packages/block-library/src/image/editor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/image/editor.scss b/packages/block-library/src/image/editor.scss index f20f0afee5f06..93f3fcab6bc6d 100644 --- a/packages/block-library/src/image/editor.scss +++ b/packages/block-library/src/image/editor.scss @@ -66,7 +66,7 @@ figure.wp-block-image:not(.wp-block) { // This is necessary for the editor resize handles to accurately work on a non-floated, non-resized, small image. .wp-block-image .components-resizable-box__container { - display: inline-block; + display: table; img { display: block; width: inherit; From e10a6450c4082def204719dfaa64fbb56ac51b02 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Fri, 14 Oct 2022 18:51:52 +0300 Subject: [PATCH 4/9] add comment --- packages/block-library/src/image/editor.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/block-library/src/image/editor.scss b/packages/block-library/src/image/editor.scss index 93f3fcab6bc6d..3e4bb7dd6392f 100644 --- a/packages/block-library/src/image/editor.scss +++ b/packages/block-library/src/image/editor.scss @@ -66,6 +66,9 @@ figure.wp-block-image:not(.wp-block) { // This is necessary for the editor resize handles to accurately work on a non-floated, non-resized, small image. .wp-block-image .components-resizable-box__container { + // Using "display: table" because: + // - it visually hides empty white space in between elements + // - it allows the element to be as wide as its contents (instead of 100% width, as it would be with `display: block`) display: table; img { display: block; From 79caffe0749b74c7bd431ad068b5cedf7a3af5f3 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Tue, 18 Oct 2022 10:07:29 +0300 Subject: [PATCH 5/9] make button toggle --- packages/block-library/src/image/image.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index ee359b7484605..7b8dc5540ce7e 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -333,14 +333,23 @@ export default function Image( { onChange={ updateAlignment } /> ) } - { ! isContentLocked && ! caption && ( + { ! isContentLocked && ( { setShowCaption( ! showCaption ); + if ( + showCaption && + ! RichText.isEmpty( caption ) + ) { + setAttributes( { caption: undefined } ); + } } } icon={ captionIcon } - disabled={ showCaption } - label={ __( 'Add caption' ) } + label={ + showCaption + ? __( 'Remove caption' ) + : __( 'Add caption' ) + } /> ) } { ! multiImageSelection && ! isEditingImage && ( From 69a7db8a306e800b00a62498255c05bc664f8513 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Tue, 18 Oct 2022 10:13:59 +0300 Subject: [PATCH 6/9] add aria-pressed --- packages/block-library/src/image/image.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 7b8dc5540ce7e..8a644043c2b09 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -337,14 +337,12 @@ export default function Image( { { setShowCaption( ! showCaption ); - if ( - showCaption && - ! RichText.isEmpty( caption ) - ) { + if ( showCaption && ! caption ) { setAttributes( { caption: undefined } ); } } } icon={ captionIcon } + isPressed={ showCaption } label={ showCaption ? __( 'Remove caption' ) From f6b0cef9f8b8eb86856c1760be69e0e1c699d99f Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Tue, 18 Oct 2022 17:53:23 +0300 Subject: [PATCH 7/9] update label --- packages/block-library/src/image/image.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 8a644043c2b09..bcfb26a914949 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -343,11 +343,7 @@ export default function Image( { } } icon={ captionIcon } isPressed={ showCaption } - label={ - showCaption - ? __( 'Remove caption' ) - : __( 'Add caption' ) - } + label={ __( 'Caption' ) } /> ) } { ! multiImageSelection && ! isEditingImage && ( From b394d918d6855919ee4425952fd497fd253b6de6 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Tue, 18 Oct 2022 18:16:01 +0300 Subject: [PATCH 8/9] update tests --- packages/e2e-tests/specs/editor/blocks/gallery.test.js | 2 +- test/e2e/specs/editor/blocks/image.spec.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/e2e-tests/specs/editor/blocks/gallery.test.js b/packages/e2e-tests/specs/editor/blocks/gallery.test.js index 79bd8a3441d1b..068975ba56409 100644 --- a/packages/e2e-tests/specs/editor/blocks/gallery.test.js +++ b/packages/e2e-tests/specs/editor/blocks/gallery.test.js @@ -111,7 +111,7 @@ describe( 'Gallery', () => { const imageListLink = ( await getListViewBlocks( 'Image' ) )[ 0 ]; await imageListLink.click(); - await clickBlockToolbarButton( 'Add caption' ); + await clickBlockToolbarButton( 'Caption' ); const captionElement = await figureElement.$( '.block-editor-rich-text__editable' ); diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index 209511d2d1cfa..0413aa3e33e01 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -157,7 +157,7 @@ test.describe( 'Image', () => { imageBlock.locator( 'data-testid=form-file-upload-input' ) ); await expect( image ).toHaveAttribute( 'src', new RegExp( filename ) ); - await editor.clickBlockToolbarButton( 'Add caption' ); + await editor.clickBlockToolbarButton( 'Caption' ); await page.keyboard.type( '1' ); await page.keyboard.press( 'Enter' ); await page.keyboard.press( 'Backspace' ); @@ -186,7 +186,7 @@ test.describe( 'Image', () => { await expect( image ).toBeVisible(); await expect( image ).toHaveAttribute( 'src', new RegExp( fileName ) ); - await editor.clickBlockToolbarButton( 'Add caption' ); + await editor.clickBlockToolbarButton( 'Caption' ); await page.keyboard.type( '12' ); await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.press( 'Enter' ); @@ -217,7 +217,7 @@ test.describe( 'Image', () => { await expect( image ).toHaveAttribute( 'src', new RegExp( fileName ) ); // Add caption and navigate to inline toolbar. - await editor.clickBlockToolbarButton( 'Add caption' ); + await editor.clickBlockToolbarButton( 'Caption' ); await pageUtils.pressKeyWithModifier( 'shift', 'Tab' ); await expect( await page.evaluate( () => From a7bdaf4c2030f8ee934509c042e9a0d70a491f2a Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Wed, 19 Oct 2022 10:01:25 +0300 Subject: [PATCH 9/9] fix reset --- packages/block-library/src/image/image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index bcfb26a914949..12e7f6dd96d2f 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -337,7 +337,7 @@ export default function Image( { { setShowCaption( ! showCaption ); - if ( showCaption && ! caption ) { + if ( showCaption && caption ) { setAttributes( { caption: undefined } ); } } }