Skip to content

Commit

Permalink
[Block Library - Image]: Add toolbar button to add a caption (#44965)
Browse files Browse the repository at this point in the history
* [Block Library - Image]: Add toolbar button to add a caption

* update tests

* change resizable box container css.display

* add comment

* make button toggle

* add aria-pressed

* update label

* update tests

* fix reset
  • Loading branch information
ntsekouras authored Oct 19, 2022
1 parent ba01e5d commit b4d463e
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 36 deletions.
5 changes: 4 additions & 1 deletion packages/block-library/src/image/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ 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;
// 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;
width: inherit;
Expand Down
88 changes: 59 additions & 29 deletions packages/block-library/src/image/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -319,6 +333,19 @@ export default function Image( {
onChange={ updateAlignment }
/>
) }
{ ! isContentLocked && (
<ToolbarButton
onClick={ () => {
setShowCaption( ! showCaption );
if ( showCaption && caption ) {
setAttributes( { caption: undefined } );
}
} }
icon={ captionIcon }
isPressed={ showCaption }
label={ __( 'Caption' ) }
/>
) }
{ ! multiImageSelection && ! isEditingImage && (
<ImageURLInputUI
url={ href || '' }
Expand Down Expand Up @@ -591,25 +618,28 @@ export default function Image( {
which causes duplicated image upload. */ }
{ ! temporaryURL && controls }
{ img }
{ ( ! RichText.isEmpty( caption ) || isSelected ) && (
<RichText
className={ __experimentalGetElementClassName( 'caption' ) }
ref={ captionRef }
tagName="figcaption"
aria-label={ __( 'Image caption text' ) }
placeholder={ __( 'Add caption' ) }
value={ caption }
onChange={ ( value ) =>
setAttributes( { caption: value } )
}
inlineToolbar
__unstableOnSplitAtEnd={ () =>
insertBlocksAfter(
createBlock( getDefaultBlockName() )
)
}
/>
) }
{ showCaption &&
( ! RichText.isEmpty( caption ) || isSelected ) && (
<RichText
className={ __experimentalGetElementClassName(
'caption'
) }
ref={ captionRef }
tagName="figcaption"
aria-label={ __( 'Image caption text' ) }
placeholder={ __( 'Add caption' ) }
value={ caption }
onChange={ ( value ) =>
setAttributes( { caption: value } )
}
inlineToolbar
__unstableOnSplitAtEnd={ () =>
insertBlocksAfter(
createBlock( getDefaultBlockName() )
)
}
/>
) }
</ImageEditingProvider>
);
}
3 changes: 2 additions & 1 deletion packages/e2e-tests/specs/editor/blocks/gallery.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
clickButton,
openListView,
getListViewBlocks,
clickBlockToolbarButton,
} from '@wordpress/e2e-test-utils';

async function upload( selector ) {
Expand Down Expand Up @@ -110,7 +111,7 @@ describe( 'Gallery', () => {

const imageListLink = ( await getListViewBlocks( 'Image' ) )[ 0 ];
await imageListLink.click();

await clickBlockToolbarButton( 'Caption' );
const captionElement = await figureElement.$(
'.block-editor-rich-text__editable'
);
Expand Down
1 change: 1 addition & 0 deletions packages/icons/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
16 changes: 16 additions & 0 deletions packages/icons/src/library/caption.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* WordPress dependencies
*/
import { Path, SVG } from '@wordpress/primitives';

const caption = (
<SVG viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M6 5.5h12a.5.5 0 0 1 .5.5v12a.5.5 0 0 1-.5.5H6a.5.5 0 0 1-.5-.5V6a.5.5 0 0 1 .5-.5ZM4 6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6Zm4 10h2v-1.5H8V16Zm5 0h-2v-1.5h2V16Zm1 0h2v-1.5h-2V16Z"
/>
</SVG>
);

export default caption;
11 changes: 6 additions & 5 deletions test/e2e/specs/editor/blocks/image.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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( 'Caption' );
await page.keyboard.type( '1' );
await page.keyboard.press( 'Enter' );
await page.keyboard.press( 'Backspace' );
Expand Down Expand Up @@ -186,7 +186,7 @@ test.describe( 'Image', () => {

await expect( image ).toBeVisible();
await expect( image ).toHaveAttribute( 'src', new RegExp( fileName ) );

await editor.clickBlockToolbarButton( 'Caption' );
await page.keyboard.type( '12' );
await page.keyboard.press( 'ArrowLeft' );
await page.keyboard.press( 'Enter' );
Expand Down Expand Up @@ -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( 'Caption' );
await pageUtils.pressKeyWithModifier( 'shift', 'Tab' );
await expect(
await page.evaluate( () =>
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit b4d463e

Please sign in to comment.