Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Block Library - Image]: Add toolbar button to add a caption #44965

Merged
merged 9 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
// 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 &&
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
( ! 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