Skip to content

Commit c861f94

Browse files
Add/gallery block caption (WordPress#17101)
* Basic gallery caption. * add block deprecation * Regenerate test fixtures and fix failures * e2e snapshot update * Fix editor styles * Fix block transforms e2e * Workaround for failing tests * Undo package-lock changes * Revert "Undo package-lock changes" This reverts commit f4f8ba9. * Fix messed up styles. * Try to fix package-lock issue _again_ * Addressed design feedback. * Addressed previous PR feedback * Addressed latest PR feedback * Update post content and improve selectors * Change selectors for captions. * Change selectors and focus behaviour. * Update fixture. * Update demo post content again. * Update e2e snapshots * Redo snapshots again. * Change conditional hiding on empty/unselected * Remove unnecessary local state
1 parent ed5e063 commit c861f94

32 files changed

+444
-121
lines changed

packages/block-library/src/gallery/block.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"type": "array",
77
"default": [ ],
88
"source": "query",
9-
"selector": "ul.wp-block-gallery .blocks-gallery-item",
9+
"selector": ".blocks-gallery-item",
1010
"query": {
1111
"url": {
1212
"source": "attribute",
@@ -37,7 +37,7 @@
3737
"caption": {
3838
"type": "string",
3939
"source": "html",
40-
"selector": "figcaption"
40+
"selector": ".blocks-gallery-item__caption"
4141
}
4242
}
4343
},
@@ -48,6 +48,11 @@
4848
"columns": {
4949
"type": "number"
5050
},
51+
"caption": {
52+
"type": "string",
53+
"source": "html",
54+
"selector": ".blocks-gallery-caption"
55+
},
5156
"imageCrop": {
5257
"type": "boolean",
5358
"default": true

packages/block-library/src/gallery/deprecated.js

+99
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,105 @@ import { RichText } from '@wordpress/block-editor';
1515
import { defaultColumnsNumber } from './shared';
1616

1717
const deprecated = [
18+
{
19+
attributes: {
20+
images: {
21+
type: 'array',
22+
default: [],
23+
source: 'query',
24+
selector: 'ul.wp-block-gallery .blocks-gallery-item',
25+
query: {
26+
url: {
27+
source: 'attribute',
28+
selector: 'img',
29+
attribute: 'src',
30+
},
31+
fullUrl: {
32+
source: 'attribute',
33+
selector: 'img',
34+
attribute: 'data-full-url',
35+
},
36+
alt: {
37+
source: 'attribute',
38+
selector: 'img',
39+
attribute: 'alt',
40+
default: '',
41+
},
42+
id: {
43+
source: 'attribute',
44+
selector: 'img',
45+
attribute: 'data-id',
46+
},
47+
link: {
48+
source: 'attribute',
49+
selector: 'img',
50+
attribute: 'data-link',
51+
},
52+
caption: {
53+
type: 'array',
54+
source: 'children',
55+
selector: 'figcaption',
56+
},
57+
},
58+
},
59+
ids: {
60+
type: 'array',
61+
default: [],
62+
},
63+
columns: {
64+
type: 'number',
65+
},
66+
imageCrop: {
67+
type: 'boolean',
68+
default: true,
69+
},
70+
linkTo: {
71+
type: 'string',
72+
default: 'none',
73+
},
74+
},
75+
save( { attributes } ) {
76+
const { images, columns = defaultColumnsNumber( attributes ), imageCrop, linkTo } = attributes;
77+
return (
78+
<ul className={ `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }` } >
79+
{ images.map( ( image ) => {
80+
let href;
81+
82+
switch ( linkTo ) {
83+
case 'media':
84+
href = image.fullUrl || image.url;
85+
break;
86+
case 'attachment':
87+
href = image.link;
88+
break;
89+
}
90+
91+
const img = (
92+
<img
93+
src={ image.url }
94+
alt={ image.alt }
95+
data-id={ image.id }
96+
data-full-url={ image.fullUrl }
97+
data-link={ image.link }
98+
className={ image.id ? `wp-image-${ image.id }` : null }
99+
/>
100+
);
101+
102+
return (
103+
<li key={ image.id || image.url } className="blocks-gallery-item">
104+
<figure>
105+
{ href ? <a href={ href }>{ img }</a> : img }
106+
{ image.caption && image.caption.length > 0 && (
107+
<RichText.Content tagName="figcaption" value={ image.caption } />
108+
) }
109+
</figure>
110+
</li>
111+
);
112+
} ) }
113+
</ul>
114+
);
115+
},
116+
},
18117
{
19118
attributes: {
20119
images: {

packages/block-library/src/gallery/edit.js

+60-34
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
BlockIcon,
2727
MediaPlaceholder,
2828
InspectorControls,
29+
RichText,
2930
} from '@wordpress/block-editor';
3031
import { Component } from '@wordpress/element';
3132
import { __, sprintf } from '@wordpress/i18n';
@@ -63,6 +64,7 @@ class GalleryEdit extends Component {
6364
this.onUploadError = this.onUploadError.bind( this );
6465
this.setImageAttributes = this.setImageAttributes.bind( this );
6566
this.setAttributes = this.setAttributes.bind( this );
67+
this.onFocusGalleryCaption = this.onFocusGalleryCaption.bind( this );
6668

6769
this.state = {
6870
selectedImage: null,
@@ -198,6 +200,12 @@ class GalleryEdit extends Component {
198200
return checked ? __( 'Thumbnails are cropped to align.' ) : __( 'Thumbnails are not cropped.' );
199201
}
200202

203+
onFocusGalleryCaption() {
204+
this.setState( {
205+
selectedImage: null,
206+
} );
207+
}
208+
201209
setImageAttributes( index, attributes ) {
202210
const { attributes: { images } } = this.props;
203211
const { setAttributes } = this;
@@ -246,10 +254,12 @@ class GalleryEdit extends Component {
246254
className,
247255
isSelected,
248256
noticeUI,
257+
setAttributes,
249258
} = this.props;
250259
const {
251260
align,
252261
columns = defaultColumnsNumber( attributes ),
262+
caption,
253263
imageCrop,
254264
images,
255265
linkTo,
@@ -283,6 +293,12 @@ class GalleryEdit extends Component {
283293
return mediaPlaceholder;
284294
}
285295

296+
const captionClassNames = classnames(
297+
'blocks-gallery-caption',
298+
{
299+
'screen-reader-text': ! isSelected && RichText.isEmpty( caption ),
300+
}
301+
);
286302
return (
287303
<>
288304
<InspectorControls>
@@ -310,42 +326,52 @@ class GalleryEdit extends Component {
310326
</PanelBody>
311327
</InspectorControls>
312328
{ noticeUI }
313-
<ul
314-
className={ classnames(
315-
className,
316-
{
317-
[ `align${ align }` ]: align,
318-
[ `columns-${ columns }` ]: columns,
319-
'is-cropped': imageCrop,
320-
}
321-
) }
329+
<figure className={ classnames(
330+
className,
331+
{
332+
[ `align${ align }` ]: align,
333+
[ `columns-${ columns }` ]: columns,
334+
'is-cropped': imageCrop,
335+
}
336+
) }
322337
>
323-
{ images.map( ( img, index ) => {
338+
<ul className="blocks-gallery-grid">
339+
{ images.map( ( img, index ) => {
324340
/* translators: %1$d is the order number of the image, %2$d is the total number of images. */
325-
const ariaLabel = sprintf( __( 'image %1$d of %2$d in gallery' ), ( index + 1 ), images.length );
326-
327-
return (
328-
<li className="blocks-gallery-item" key={ img.id || img.url }>
329-
<GalleryImage
330-
url={ img.url }
331-
alt={ img.alt }
332-
id={ img.id }
333-
isFirstItem={ index === 0 }
334-
isLastItem={ ( index + 1 ) === images.length }
335-
isSelected={ isSelected && this.state.selectedImage === index }
336-
onMoveBackward={ this.onMoveBackward( index ) }
337-
onMoveForward={ this.onMoveForward( index ) }
338-
onRemove={ this.onRemoveImage( index ) }
339-
onSelect={ this.onSelectImage( index ) }
340-
setAttributes={ ( attrs ) => this.setImageAttributes( index, attrs ) }
341-
caption={ img.caption }
342-
aria-label={ ariaLabel }
343-
/>
344-
</li>
345-
);
346-
} ) }
347-
</ul>
348-
{ mediaPlaceholder }
341+
const ariaLabel = sprintf( __( 'image %1$d of %2$d in gallery' ), ( index + 1 ), images.length );
342+
343+
return (
344+
<li className="blocks-gallery-item" key={ img.id || img.url }>
345+
<GalleryImage
346+
url={ img.url }
347+
alt={ img.alt }
348+
id={ img.id }
349+
isFirstItem={ index === 0 }
350+
isLastItem={ ( index + 1 ) === images.length }
351+
isSelected={ isSelected && this.state.selectedImage === index }
352+
onMoveBackward={ this.onMoveBackward( index ) }
353+
onMoveForward={ this.onMoveForward( index ) }
354+
onRemove={ this.onRemoveImage( index ) }
355+
onSelect={ this.onSelectImage( index ) }
356+
setAttributes={ ( attrs ) => this.setImageAttributes( index, attrs ) }
357+
caption={ img.caption }
358+
aria-label={ ariaLabel }
359+
/>
360+
</li>
361+
);
362+
} ) }
363+
</ul>
364+
{ mediaPlaceholder }
365+
<RichText
366+
tagName="figcaption"
367+
className={ captionClassNames }
368+
placeholder={ __( 'Write gallery caption…' ) }
369+
value={ caption }
370+
unstableOnFocus={ this.onFocusGalleryCaption }
371+
onChange={ ( value ) => setAttributes( { caption: value } ) }
372+
inlineToolbar
373+
/>
374+
</figure>
349375
</>
350376
);
351377
}

packages/block-library/src/gallery/editor.scss

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ul.wp-block-gallery {
1+
.wp-block-gallery {
22
// Override the default list style type _only in the editor_
33
// to avoid :not() selector specificity issues.
44
// See https://github.com/WordPress/gutenberg/pull/10358
@@ -12,6 +12,11 @@ ul.wp-block-gallery {
1212
}
1313
}
1414

15+
// need to override default editor ul styles
16+
.blocks-gallery-grid.blocks-gallery-grid {
17+
margin-bottom: 0;
18+
}
19+
1520
.blocks-gallery-item {
1621

1722
// Hide the focus outline that otherwise briefly appears when selecting a block.

packages/block-library/src/gallery/save.js

+38-34
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,47 @@ import { RichText } from '@wordpress/block-editor';
99
import { defaultColumnsNumber } from './shared';
1010

1111
export default function save( { attributes } ) {
12-
const { images, columns = defaultColumnsNumber( attributes ), imageCrop, linkTo } = attributes;
12+
const { images, columns = defaultColumnsNumber( attributes ), imageCrop, caption, linkTo } = attributes;
13+
1314
return (
14-
<ul className={ `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }` } >
15-
{ images.map( ( image ) => {
16-
let href;
15+
<figure className={ `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }` }>
16+
<ul className="blocks-gallery-grid">
17+
{ images.map( ( image ) => {
18+
let href;
1719

18-
switch ( linkTo ) {
19-
case 'media':
20-
href = image.fullUrl || image.url;
21-
break;
22-
case 'attachment':
23-
href = image.link;
24-
break;
25-
}
20+
switch ( linkTo ) {
21+
case 'media':
22+
href = image.fullUrl || image.url;
23+
break;
24+
case 'attachment':
25+
href = image.link;
26+
break;
27+
}
2628

27-
const img = (
28-
<img
29-
src={ image.url }
30-
alt={ image.alt }
31-
data-id={ image.id }
32-
data-full-url={ image.fullUrl }
33-
data-link={ image.link }
34-
className={ image.id ? `wp-image-${ image.id }` : null }
35-
/>
36-
);
29+
const img = (
30+
<img
31+
src={ image.url }
32+
alt={ image.alt }
33+
data-id={ image.id }
34+
data-full-url={ image.fullUrl }
35+
data-link={ image.link }
36+
className={ image.id ? `wp-image-${ image.id }` : null }
37+
/>
38+
);
3739

38-
return (
39-
<li key={ image.id || image.url } className="blocks-gallery-item">
40-
<figure>
41-
{ href ? <a href={ href }>{ img }</a> : img }
42-
{ image.caption && image.caption.length > 0 && (
43-
<RichText.Content tagName="figcaption" value={ image.caption } />
44-
) }
45-
</figure>
46-
</li>
47-
);
48-
} ) }
49-
</ul>
40+
return (
41+
<li key={ image.id || image.url } className="blocks-gallery-item">
42+
<figure>
43+
{ href ? <a href={ href }>{ img }</a> : img }
44+
{ ! RichText.isEmpty( image.caption ) && (
45+
<RichText.Content tagName="figcaption" className="blocks-gallery-item__caption" value={ image.caption } />
46+
) }
47+
</figure>
48+
</li>
49+
);
50+
} ) }
51+
</ul>
52+
{ ! RichText.isEmpty( caption ) && <RichText.Content tagName="figcaption" className="blocks-gallery-caption" value={ caption } /> }
53+
</figure>
5054
);
5155
}

packages/block-library/src/gallery/style.scss

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
.wp-block-gallery {
1+
.wp-block-gallery,
2+
.blocks-gallery-grid {
23
display: flex;
34
flex-wrap: wrap;
45
list-style-type: none;
56
padding: 0;
7+
margin-bottom: 0;
68

79
.blocks-gallery-image,
810
.blocks-gallery-item {
@@ -148,3 +150,8 @@
148150
}
149151
}
150152
}
153+
154+
figure.wp-block-gallery {
155+
display: block;
156+
margin: 0;
157+
}

0 commit comments

Comments
 (0)