@wordpress/block-library 10.4.0 → 10.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/build/accordion/view.cjs +2 -5
- package/build/accordion/view.cjs.map +2 -2
- package/build/accordion-heading/block.json +1 -0
- package/build/audio/edit.cjs.map +2 -2
- package/build/avatar/edit.cjs.map +2 -2
- package/build/block/block.json +2 -1
- package/build/column/edit.cjs +1 -1
- package/build/column/edit.cjs.map +2 -2
- package/build/columns/edit.cjs.map +2 -2
- package/build/columns/transforms.cjs +161 -2
- package/build/columns/transforms.cjs.map +2 -2
- package/build/cover/edit/block-controls.cjs +1 -1
- package/build/cover/edit/block-controls.cjs.map +2 -2
- package/build/cover/edit/index.cjs +1 -1
- package/build/cover/edit/index.cjs.map +2 -2
- package/build/cover/edit/inspector-controls.cjs.map +3 -3
- package/build/embed/embed-placeholder.cjs.map +2 -2
- package/build/gallery/transforms.cjs +8 -0
- package/build/gallery/transforms.cjs.map +2 -2
- package/build/group/block.json +1 -1
- package/build/heading/transforms.cjs +36 -0
- package/build/heading/transforms.cjs.map +2 -2
- package/build/heading/variations.cjs +13 -1
- package/build/heading/variations.cjs.map +2 -2
- package/build/home-link/edit.cjs.map +3 -3
- package/build/icon/edit.cjs +7 -1
- package/build/icon/edit.cjs.map +2 -2
- package/build/image/image.cjs +1 -1
- package/build/image/image.cjs.map +3 -3
- package/build/list/block.json +1 -0
- package/build/math/edit.cjs +4 -5
- package/build/math/edit.cjs.map +3 -3
- package/build/media-text/edit.cjs.map +3 -3
- package/build/more/block.json +1 -1
- package/build/navigation-link/shared/controls.cjs.map +3 -3
- package/build/playlist/block.json +0 -1
- package/build/playlist/edit.cjs +46 -22
- package/build/playlist/edit.cjs.map +2 -2
- package/build/playlist-track/block.json +16 -8
- package/build/playlist-track/edit.cjs +74 -67
- package/build/playlist-track/edit.cjs.map +3 -3
- package/build/post-template/edit.cjs +5 -1
- package/build/post-template/edit.cjs.map +2 -2
- package/build/post-title/edit.cjs +3 -8
- package/build/post-title/edit.cjs.map +2 -2
- package/build/private-apis.cjs +0 -12
- package/build/private-apis.cjs.map +3 -3
- package/build/query/block.json +3 -0
- package/build/query-no-results/block.json +16 -1
- package/build/rss/edit.cjs.map +2 -2
- package/build/site-logo/edit.cjs +1 -1
- package/build/site-logo/edit.cjs.map +2 -2
- package/build/site-title/edit.cjs +2 -6
- package/build/site-title/edit.cjs.map +2 -2
- package/build/table-of-contents/block.json +2 -1
- package/build/table-of-contents/deprecated.cjs +165 -0
- package/build/table-of-contents/deprecated.cjs.map +7 -0
- package/build/table-of-contents/index.cjs +2 -2
- package/build/table-of-contents/index.cjs.map +3 -3
- package/build/tabs/view.cjs +32 -2
- package/build/tabs/view.cjs.map +2 -2
- package/build/term-template/edit.cjs +5 -1
- package/build/term-template/edit.cjs.map +2 -2
- package/build-module/accordion/view.mjs +2 -5
- package/build-module/accordion/view.mjs.map +2 -2
- package/build-module/accordion-heading/block.json +1 -0
- package/build-module/audio/edit.mjs.map +2 -2
- package/build-module/avatar/edit.mjs.map +2 -2
- package/build-module/block/block.json +2 -1
- package/build-module/column/edit.mjs +1 -1
- package/build-module/column/edit.mjs.map +2 -2
- package/build-module/columns/edit.mjs.map +2 -2
- package/build-module/columns/transforms.mjs +164 -3
- package/build-module/columns/transforms.mjs.map +2 -2
- package/build-module/cover/edit/block-controls.mjs +1 -1
- package/build-module/cover/edit/block-controls.mjs.map +2 -2
- package/build-module/cover/edit/index.mjs +1 -1
- package/build-module/cover/edit/index.mjs.map +2 -2
- package/build-module/cover/edit/inspector-controls.mjs +2 -2
- package/build-module/cover/edit/inspector-controls.mjs.map +2 -2
- package/build-module/embed/embed-placeholder.mjs +2 -2
- package/build-module/embed/embed-placeholder.mjs.map +1 -1
- package/build-module/gallery/transforms.mjs +8 -0
- package/build-module/gallery/transforms.mjs.map +2 -2
- package/build-module/group/block.json +1 -1
- package/build-module/heading/transforms.mjs +36 -0
- package/build-module/heading/transforms.mjs.map +2 -2
- package/build-module/heading/variations.mjs +13 -1
- package/build-module/heading/variations.mjs.map +2 -2
- package/build-module/home-link/edit.mjs +2 -2
- package/build-module/home-link/edit.mjs.map +2 -2
- package/build-module/icon/edit.mjs +7 -1
- package/build-module/icon/edit.mjs.map +2 -2
- package/build-module/image/image.mjs +4 -4
- package/build-module/image/image.mjs.map +2 -2
- package/build-module/list/block.json +1 -0
- package/build-module/math/edit.mjs +4 -8
- package/build-module/math/edit.mjs.map +2 -2
- package/build-module/media-text/edit.mjs +2 -2
- package/build-module/media-text/edit.mjs.map +2 -2
- package/build-module/more/block.json +1 -1
- package/build-module/navigation-link/shared/controls.mjs +2 -2
- package/build-module/navigation-link/shared/controls.mjs.map +2 -2
- package/build-module/playlist/block.json +0 -1
- package/build-module/playlist/edit.mjs +46 -22
- package/build-module/playlist/edit.mjs.map +2 -2
- package/build-module/playlist-track/block.json +16 -8
- package/build-module/playlist-track/edit.mjs +76 -69
- package/build-module/playlist-track/edit.mjs.map +2 -2
- package/build-module/post-template/edit.mjs +5 -1
- package/build-module/post-template/edit.mjs.map +2 -2
- package/build-module/post-title/edit.mjs +3 -8
- package/build-module/post-title/edit.mjs.map +2 -2
- package/build-module/private-apis.mjs +0 -2
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/query/block.json +3 -0
- package/build-module/query-no-results/block.json +16 -1
- package/build-module/rss/edit.mjs +2 -2
- package/build-module/rss/edit.mjs.map +1 -1
- package/build-module/site-logo/edit.mjs +1 -1
- package/build-module/site-logo/edit.mjs.map +2 -2
- package/build-module/site-title/edit.mjs +2 -6
- package/build-module/site-title/edit.mjs.map +2 -2
- package/build-module/table-of-contents/block.json +2 -1
- package/build-module/table-of-contents/deprecated.mjs +144 -0
- package/build-module/table-of-contents/deprecated.mjs.map +7 -0
- package/build-module/table-of-contents/index.mjs +2 -2
- package/build-module/table-of-contents/index.mjs.map +2 -2
- package/build-module/tabs/view.mjs +32 -2
- package/build-module/tabs/view.mjs.map +2 -2
- package/build-module/term-template/edit.mjs +5 -1
- package/build-module/term-template/edit.mjs.map +2 -2
- package/build-style/categories/style-rtl.css +2 -1
- package/build-style/categories/style.css +2 -1
- package/build-style/comments/editor-rtl.css +8 -10
- package/build-style/comments/editor.css +8 -10
- package/build-style/comments/style-rtl.css +8 -7
- package/build-style/comments/style.css +8 -7
- package/build-style/editor-rtl.css +8 -10
- package/build-style/editor.css +8 -10
- package/build-style/form-input/style-rtl.css +7 -7
- package/build-style/form-input/style.css +7 -7
- package/build-style/post-comments-form/style-rtl.css +7 -6
- package/build-style/post-comments-form/style.css +7 -6
- package/build-style/query-no-results/style-rtl.css +3 -0
- package/build-style/query-no-results/style.css +3 -0
- package/build-style/search/style-rtl.css +1 -1
- package/build-style/search/style.css +1 -1
- package/build-style/search/theme-rtl.css +1 -1
- package/build-style/search/theme.css +1 -1
- package/build-style/style-rtl.css +34 -23
- package/build-style/style.css +34 -23
- package/build-style/tab-panel/style-rtl.css +5 -1
- package/build-style/tab-panel/style.css +5 -1
- package/build-style/theme-rtl.css +1 -1
- package/build-style/theme.css +1 -1
- package/package.json +43 -43
- package/src/accordion/view.js +2 -5
- package/src/accordion-heading/README.md +1 -0
- package/src/accordion-heading/block.json +1 -0
- package/src/audio/edit.js +13 -0
- package/src/avatar/edit.js +78 -0
- package/src/block/README.md +1 -0
- package/src/block/block.json +2 -1
- package/src/categories/style.scss +4 -4
- package/src/column/edit.js +15 -1
- package/src/columns/edit.js +18 -0
- package/src/columns/test/transforms.js +517 -1
- package/src/columns/transforms.js +207 -2
- package/src/comments/style.scss +7 -10
- package/src/cover/edit/block-controls.js +1 -1
- package/src/cover/edit/index.js +1 -1
- package/src/cover/edit/inspector-controls.js +2 -2
- package/src/embed/embed-placeholder.js +2 -2
- package/src/form-input/style.scss +2 -2
- package/src/gallery/test/edit.js +57 -0
- package/src/gallery/test/transforms.js +57 -0
- package/src/gallery/transforms.js +8 -0
- package/src/group/README.md +1 -1
- package/src/group/block.json +1 -1
- package/src/heading/transforms.js +36 -0
- package/src/heading/variations.js +12 -0
- package/src/home-link/edit.js +2 -2
- package/src/icon/edit.js +9 -1
- package/src/image/image.js +4 -4
- package/src/list/README.md +1 -0
- package/src/list/block.json +1 -0
- package/src/math/edit.js +4 -9
- package/src/media-text/edit.js +2 -2
- package/src/more/README.md +1 -1
- package/src/more/block.json +1 -1
- package/src/navigation/index.php +8 -2
- package/src/navigation-link/shared/controls.js +2 -2
- package/src/playlist/block.json +0 -1
- package/src/playlist/edit.js +54 -21
- package/src/playlist/test/edit-component.js +78 -27
- package/src/playlist-track/README.md +8 -8
- package/src/playlist-track/block.json +16 -8
- package/src/playlist-track/edit.js +56 -27
- package/src/playlist-track/test/edit.js +145 -41
- package/src/post-comments-form/style.scss +7 -9
- package/src/post-template/edit.js +10 -1
- package/src/post-title/edit.js +2 -9
- package/src/private-apis.js +0 -2
- package/src/query/README.md +2 -0
- package/src/query/block.json +3 -0
- package/src/query-no-results/README.md +3 -0
- package/src/query-no-results/block.json +16 -1
- package/src/query-no-results/style.scss +5 -0
- package/src/rss/edit.js +2 -2
- package/src/search/style.scss +1 -1
- package/src/search/theme.scss +2 -4
- package/src/site-logo/edit.js +1 -1
- package/src/site-title/edit.js +1 -10
- package/src/social-link/index.php +6 -13
- package/src/style.scss +1 -0
- package/src/tab-panel/index.php +14 -5
- package/src/tab-panel/style.scss +7 -1
- package/src/table-of-contents/README.md +6 -6
- package/src/table-of-contents/block.json +2 -1
- package/src/table-of-contents/deprecated.js +173 -0
- package/src/table-of-contents/index.js +2 -2
- package/src/table-of-contents/index.php +16 -3
- package/src/tabs/index.php +5 -5
- package/src/tabs/view.js +32 -2
- package/src/term-description/index.php +1 -1
- package/src/term-template/edit.js +10 -1
- package/src/utils/test/waveform-player-dependency.js +0 -74
- package/build/block-keyboard-shortcuts/index.cjs +0 -131
- package/build/block-keyboard-shortcuts/index.cjs.map +0 -7
- package/build/table-of-contents/save.cjs +0 -55
- package/build/table-of-contents/save.cjs.map +0 -7
- package/build-module/block-keyboard-shortcuts/index.mjs +0 -113
- package/build-module/block-keyboard-shortcuts/index.mjs.map +0 -7
- package/build-module/table-of-contents/save.mjs +0 -24
- package/build-module/table-of-contents/save.mjs.map +0 -7
- package/src/block-keyboard-shortcuts/index.js +0 -135
- package/src/table-of-contents/save.js +0 -22
package/src/list/block.json
CHANGED
package/src/math/edit.js
CHANGED
|
@@ -3,15 +3,10 @@ import {
|
|
|
3
3
|
useBlockProps,
|
|
4
4
|
store as blockEditorStore,
|
|
5
5
|
} from '@wordpress/block-editor';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
privateApis as componentsPrivateApis,
|
|
9
|
-
} from '@wordpress/components';
|
|
6
|
+
import { Popover } from '@wordpress/components';
|
|
7
|
+
import { ValidatedTextareaControl } from '@wordpress/ui';
|
|
10
8
|
import { useState, useEffect, useRef } from '@wordpress/element';
|
|
11
9
|
import { useDispatch } from '@wordpress/data';
|
|
12
|
-
import { unlock } from '../lock-unlock';
|
|
13
|
-
|
|
14
|
-
const { ValidatedTextareaControl } = unlock( componentsPrivateApis );
|
|
15
10
|
|
|
16
11
|
export default function MathEdit( { attributes, setAttributes, isSelected } ) {
|
|
17
12
|
const { latex, mathML } = attributes;
|
|
@@ -78,14 +73,14 @@ export default function MathEdit( { attributes, setAttributes, isSelected } ) {
|
|
|
78
73
|
<ValidatedTextareaControl
|
|
79
74
|
label={ __( 'LaTeX math syntax' ) }
|
|
80
75
|
hideLabelFromVision
|
|
81
|
-
value={ latex }
|
|
76
|
+
value={ latex ?? '' }
|
|
82
77
|
className="wp-block-math__textarea-control"
|
|
83
78
|
customValidity={
|
|
84
79
|
error
|
|
85
80
|
? { type: 'invalid', message: error }
|
|
86
81
|
: undefined
|
|
87
82
|
}
|
|
88
|
-
|
|
83
|
+
onValueChange={ ( newLatex ) => {
|
|
89
84
|
if ( ! latexToMathML ) {
|
|
90
85
|
setAttributes( { latex: newLatex } );
|
|
91
86
|
return;
|
package/src/media-text/edit.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from '@wordpress/block-editor';
|
|
16
16
|
import {
|
|
17
17
|
RangeControl,
|
|
18
|
-
TextareaControl,
|
|
18
|
+
TextareaControl as WCTextareaControl,
|
|
19
19
|
ToggleControl,
|
|
20
20
|
ToolbarButton,
|
|
21
21
|
ExternalLink,
|
|
@@ -431,7 +431,7 @@ function MediaTextEdit( {
|
|
|
431
431
|
hasValue={ () => !! mediaAlt }
|
|
432
432
|
onDeselect={ () => setAttributes( { mediaAlt: '' } ) }
|
|
433
433
|
>
|
|
434
|
-
<
|
|
434
|
+
<WCTextareaControl
|
|
435
435
|
label={ __( 'Alternative text' ) }
|
|
436
436
|
value={ mediaAlt }
|
|
437
437
|
onChange={ onMediaAltChange }
|
package/src/more/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# More
|
|
2
2
|
|
|
3
3
|
<!-- START TOKEN(Autogenerated block API docs) -->
|
|
4
|
-
Content before this block
|
|
4
|
+
Content before this block is shown in the excerpt on blog and archive pages. Where a theme shows the full content instead, visitors get a “Read more” link to the rest of the post.
|
|
5
5
|
|
|
6
6
|
- **Name:** `core/more`
|
|
7
7
|
- **Category:** [design](https://developer.wordpress.org/block-editor/reference-guides/core-blocks/core-blocks-design/)
|
package/src/more/block.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"name": "core/more",
|
|
5
5
|
"title": "More",
|
|
6
6
|
"category": "design",
|
|
7
|
-
"description": "Content before this block
|
|
7
|
+
"description": "Content before this block is shown in the excerpt on blog and archive pages. Where a theme shows the full content instead, visitors get a “Read more” link to the rest of the post.",
|
|
8
8
|
"keywords": [ "read more" ],
|
|
9
9
|
"textdomain": "default",
|
|
10
10
|
"attributes": {
|
package/src/navigation/index.php
CHANGED
|
@@ -279,8 +279,14 @@ class WP_Navigation_Block_Renderer {
|
|
|
279
279
|
|
|
280
280
|
foreach ( $inner_blocks as $inner_block ) {
|
|
281
281
|
$inner_block_markup = static::get_markup_for_inner_block( $inner_block );
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
// Skip hidden blocks (e.g. hidden via block visibility) that render
|
|
283
|
+
// as an empty string. Without this check, empty markup is mistaken
|
|
284
|
+
// for a non-list-item and incorrectly closes the open <ul>.
|
|
285
|
+
if ( '' === $inner_block_markup ) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
$p = new WP_HTML_Tag_Processor( $inner_block_markup );
|
|
289
|
+
$is_list_item = $p->next_tag( 'LI' );
|
|
284
290
|
|
|
285
291
|
if ( $is_list_item && ! $is_list_open ) {
|
|
286
292
|
$is_list_open = true;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
__experimentalToolsPanelItem as ToolsPanelItem,
|
|
5
5
|
CheckboxControl,
|
|
6
6
|
TextControl,
|
|
7
|
-
TextareaControl,
|
|
7
|
+
TextareaControl as WCTextareaControl,
|
|
8
8
|
} from '@wordpress/components';
|
|
9
9
|
import { __, sprintf } from '@wordpress/i18n';
|
|
10
10
|
import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
|
|
@@ -265,7 +265,7 @@ export function Controls( {
|
|
|
265
265
|
onDeselect={ () => setAttributes( { description: '' } ) }
|
|
266
266
|
isShownByDefault={ ! isContentOnly }
|
|
267
267
|
>
|
|
268
|
-
<
|
|
268
|
+
<WCTextareaControl
|
|
269
269
|
label={ __( 'Description' ) }
|
|
270
270
|
value={ description || '' }
|
|
271
271
|
onChange={ ( descriptionValue ) => {
|
package/src/playlist/block.json
CHANGED
package/src/playlist/edit.js
CHANGED
|
@@ -86,7 +86,13 @@ const PlaylistEdit = ( {
|
|
|
86
86
|
|
|
87
87
|
const blockProps = useBlockProps();
|
|
88
88
|
const waveformPanelId = `${ clientId }-waveform`;
|
|
89
|
-
const {
|
|
89
|
+
const {
|
|
90
|
+
replaceInnerBlocks,
|
|
91
|
+
replaceBlocks,
|
|
92
|
+
insertBlocks,
|
|
93
|
+
selectBlock,
|
|
94
|
+
__unstableMarkNextChangeAsNotPersistent,
|
|
95
|
+
} = useDispatch( blockEditorStore );
|
|
90
96
|
const { createErrorNotice } = useDispatch( noticesStore );
|
|
91
97
|
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
|
|
92
98
|
const colorGradientSettings = useMultipleOriginColorsAndGradients();
|
|
@@ -164,6 +170,23 @@ const PlaylistEdit = ( {
|
|
|
164
170
|
}
|
|
165
171
|
}, [ currentTrackClientId, setCurrentTrackClientId, validTracks ] );
|
|
166
172
|
|
|
173
|
+
// Clean up placeholder tracks when reverting back to playlist placeholder state
|
|
174
|
+
useEffect( () => {
|
|
175
|
+
if ( validTracks.length > 0 || innerBlockTracks.length === 0 ) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Remove any lingering placeholder track blocks
|
|
180
|
+
__unstableMarkNextChangeAsNotPersistent();
|
|
181
|
+
replaceInnerBlocks( clientId, [] );
|
|
182
|
+
}, [
|
|
183
|
+
clientId,
|
|
184
|
+
innerBlockTracks,
|
|
185
|
+
replaceInnerBlocks,
|
|
186
|
+
validTracks,
|
|
187
|
+
__unstableMarkNextChangeAsNotPersistent,
|
|
188
|
+
] );
|
|
189
|
+
|
|
167
190
|
const createTrackBlocks = useCallback(
|
|
168
191
|
( media ) => {
|
|
169
192
|
if ( ! media ) {
|
|
@@ -248,27 +271,36 @@ const PlaylistEdit = ( {
|
|
|
248
271
|
return;
|
|
249
272
|
}
|
|
250
273
|
|
|
251
|
-
const nextBlocks = [ ...validTracks, ...newBlocks ];
|
|
252
274
|
setCurrentTrackClientId( newBlocks[ 0 ].clientId );
|
|
253
|
-
|
|
275
|
+
insertBlocks( newBlocks, undefined, clientId, false );
|
|
254
276
|
selectBlock( newBlocks[ 0 ].clientId );
|
|
255
277
|
},
|
|
256
278
|
[
|
|
257
279
|
clientId,
|
|
258
280
|
createTrackBlocks,
|
|
259
|
-
|
|
281
|
+
insertBlocks,
|
|
260
282
|
selectBlock,
|
|
261
283
|
setCurrentTrackClientId,
|
|
262
284
|
validTracks,
|
|
263
285
|
]
|
|
264
286
|
);
|
|
265
287
|
|
|
288
|
+
const removeTrack = useCallback(
|
|
289
|
+
( trackClientId ) => {
|
|
290
|
+
// Not `removeBlock`: it removes the container along with its
|
|
291
|
+
// last inner block, so this would take the playlist with it.
|
|
292
|
+
replaceBlocks( trackClientId, [] );
|
|
293
|
+
},
|
|
294
|
+
[ replaceBlocks ]
|
|
295
|
+
);
|
|
296
|
+
|
|
266
297
|
const playlistContext = useMemo(
|
|
267
298
|
() => ( {
|
|
268
299
|
currentTrackClientId,
|
|
269
300
|
setCurrentTrackClientId,
|
|
301
|
+
removeTrack,
|
|
270
302
|
} ),
|
|
271
|
-
[ currentTrackClientId, setCurrentTrackClientId ]
|
|
303
|
+
[ currentTrackClientId, setCurrentTrackClientId, removeTrack ]
|
|
272
304
|
);
|
|
273
305
|
|
|
274
306
|
// Get current track data by finding the track with matching client ID.
|
|
@@ -439,10 +471,20 @@ const PlaylistEdit = ( {
|
|
|
439
471
|
);
|
|
440
472
|
}
|
|
441
473
|
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
474
|
+
const { children: innerBlocks, ...trackListProps } = useInnerBlocksProps(
|
|
475
|
+
{
|
|
476
|
+
className: clsx( 'wp-block-playlist__tracklist', {
|
|
477
|
+
'wp-block-playlist__tracklist-is-hidden': ! showTracklist,
|
|
478
|
+
'wp-block-playlist__tracklist-show-numbers': showNumbers,
|
|
479
|
+
'wp-block-playlist__tracklist-length-is-hidden':
|
|
480
|
+
! showTrackLength,
|
|
481
|
+
} ),
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
__experimentalAppenderTagName: 'li',
|
|
485
|
+
renderAppender: false,
|
|
486
|
+
}
|
|
487
|
+
);
|
|
446
488
|
|
|
447
489
|
if ( tracks.length === 0 ) {
|
|
448
490
|
return (
|
|
@@ -471,7 +513,7 @@ const PlaylistEdit = ( {
|
|
|
471
513
|
|
|
472
514
|
return (
|
|
473
515
|
<>
|
|
474
|
-
<BlockControls group="other"
|
|
516
|
+
<BlockControls group="other">
|
|
475
517
|
<MediaReplaceFlow
|
|
476
518
|
name={ __( 'Add track' ) }
|
|
477
519
|
onSelect={ onAddTracks }
|
|
@@ -693,18 +735,9 @@ const PlaylistEdit = ( {
|
|
|
693
735
|
showPlayButtonArtwork={ showPlayButtonArtwork === true }
|
|
694
736
|
/>
|
|
695
737
|
</Disabled>
|
|
696
|
-
<ol
|
|
697
|
-
className={ clsx( 'wp-block-playlist__tracklist', {
|
|
698
|
-
'wp-block-playlist__tracklist-is-hidden':
|
|
699
|
-
! showTracklist,
|
|
700
|
-
'wp-block-playlist__tracklist-show-numbers':
|
|
701
|
-
showNumbers,
|
|
702
|
-
'wp-block-playlist__tracklist-length-is-hidden':
|
|
703
|
-
! showTrackLength,
|
|
704
|
-
} ) }
|
|
705
|
-
>
|
|
738
|
+
<ol { ...trackListProps }>
|
|
706
739
|
<PlaylistContext.Provider value={ playlistContext }>
|
|
707
|
-
{
|
|
740
|
+
{ innerBlocks }
|
|
708
741
|
</PlaylistContext.Provider>
|
|
709
742
|
</ol>
|
|
710
743
|
<Caption
|
|
@@ -4,6 +4,9 @@ import PlaylistEdit from '../edit';
|
|
|
4
4
|
|
|
5
5
|
let mediaPlaceholderProps;
|
|
6
6
|
let mediaReplaceFlowProps;
|
|
7
|
+
// The tracks the playlist holds. The inner blocks mock renders these, so a
|
|
8
|
+
// test can look at what ended up in the tracklist.
|
|
9
|
+
let mockTracks = [];
|
|
7
10
|
|
|
8
11
|
jest.mock( '@wordpress/block-editor', () => ( {
|
|
9
12
|
store: {},
|
|
@@ -33,7 +36,11 @@ jest.mock( '@wordpress/block-editor', () => ( {
|
|
|
33
36
|
useBlockProps: () => ( { className: 'wp-block-playlist' } ),
|
|
34
37
|
useInnerBlocksProps: ( blockProps ) => ( {
|
|
35
38
|
...blockProps,
|
|
36
|
-
children:
|
|
39
|
+
children: mockTracks.map( ( track ) => (
|
|
40
|
+
<li key={ track.clientId } data-testid="playlist-track">
|
|
41
|
+
{ track.attributes.title }
|
|
42
|
+
</li>
|
|
43
|
+
) ),
|
|
37
44
|
} ),
|
|
38
45
|
__experimentalColorGradientSettingsDropdown: () => <div />,
|
|
39
46
|
__experimentalUseMultipleOriginColorsAndGradients: () => ( {
|
|
@@ -106,36 +113,58 @@ const defaultAttributes = {
|
|
|
106
113
|
|
|
107
114
|
describe( 'PlaylistEdit', () => {
|
|
108
115
|
let replaceInnerBlocks;
|
|
116
|
+
let insertBlocks;
|
|
109
117
|
let selectBlock;
|
|
110
118
|
|
|
119
|
+
// The playlist reads its tracks from the store and changes them through
|
|
120
|
+
// dispatched actions. Keeping a list here lets a test assert which tracks
|
|
121
|
+
// the playlist ends up with, rather than which action it reached for.
|
|
122
|
+
const trackStore = ( initial ) => {
|
|
123
|
+
mockTracks = initial;
|
|
124
|
+
useSelect.mockImplementation( () => ( {
|
|
125
|
+
innerBlockTracks: mockTracks,
|
|
126
|
+
} ) );
|
|
127
|
+
insertBlocks.mockImplementation( ( blocks, index ) => {
|
|
128
|
+
const at = index ?? mockTracks.length;
|
|
129
|
+
mockTracks = [
|
|
130
|
+
...mockTracks.slice( 0, at ),
|
|
131
|
+
...blocks,
|
|
132
|
+
...mockTracks.slice( at ),
|
|
133
|
+
];
|
|
134
|
+
} );
|
|
135
|
+
replaceInnerBlocks.mockImplementation( ( _clientId, next ) => {
|
|
136
|
+
mockTracks = next;
|
|
137
|
+
} );
|
|
138
|
+
};
|
|
139
|
+
const trackClientIds = () => mockTracks.map( ( track ) => track.clientId );
|
|
140
|
+
|
|
111
141
|
beforeEach( () => {
|
|
112
142
|
mediaPlaceholderProps = undefined;
|
|
113
143
|
mediaReplaceFlowProps = undefined;
|
|
114
144
|
replaceInnerBlocks = jest.fn();
|
|
145
|
+
insertBlocks = jest.fn();
|
|
115
146
|
selectBlock = jest.fn();
|
|
116
147
|
useDispatch.mockReturnValue( {
|
|
117
148
|
createErrorNotice: jest.fn(),
|
|
118
149
|
replaceInnerBlocks,
|
|
150
|
+
insertBlocks,
|
|
119
151
|
selectBlock,
|
|
152
|
+
__unstableMarkNextChangeAsNotPersistent: jest.fn(),
|
|
120
153
|
} );
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
title: 'Sample track',
|
|
129
|
-
},
|
|
154
|
+
trackStore( [
|
|
155
|
+
{
|
|
156
|
+
clientId: 'track-1',
|
|
157
|
+
attributes: {
|
|
158
|
+
id: 1,
|
|
159
|
+
src: 'https://example.com/audio.mp3',
|
|
160
|
+
title: 'Sample track',
|
|
130
161
|
},
|
|
131
|
-
|
|
132
|
-
|
|
162
|
+
},
|
|
163
|
+
] );
|
|
133
164
|
} );
|
|
134
165
|
|
|
135
166
|
it( 'lets users select audio tracks individually from the Media Library', () => {
|
|
136
|
-
|
|
137
|
-
innerBlockTracks: [],
|
|
138
|
-
} );
|
|
167
|
+
trackStore( [] );
|
|
139
168
|
|
|
140
169
|
render(
|
|
141
170
|
<PlaylistEdit
|
|
@@ -186,6 +215,39 @@ describe( 'PlaylistEdit', () => {
|
|
|
186
215
|
expect( screen.getByTestId( 'playlist-track' ) ).toBeInTheDocument();
|
|
187
216
|
} );
|
|
188
217
|
|
|
218
|
+
it( 'preserves placeholder tracks when adding tracks', () => {
|
|
219
|
+
trackStore( [
|
|
220
|
+
{
|
|
221
|
+
clientId: 'track-1',
|
|
222
|
+
attributes: {
|
|
223
|
+
id: 1,
|
|
224
|
+
src: 'https://example.com/audio.mp3',
|
|
225
|
+
title: 'Sample track',
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
// A track the user added but has not filled in yet.
|
|
229
|
+
{ clientId: 'placeholder-track', attributes: {} },
|
|
230
|
+
] );
|
|
231
|
+
|
|
232
|
+
render(
|
|
233
|
+
<PlaylistEdit
|
|
234
|
+
attributes={ defaultAttributes }
|
|
235
|
+
clientId="playlist-1"
|
|
236
|
+
insertBlocksAfter={ jest.fn() }
|
|
237
|
+
isSelected={ false }
|
|
238
|
+
setAttributes={ jest.fn() }
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
fireEvent.click( screen.getByRole( 'button', { name: 'Add track' } ) );
|
|
243
|
+
|
|
244
|
+
expect( trackClientIds() ).toEqual( [
|
|
245
|
+
'track-1',
|
|
246
|
+
'placeholder-track',
|
|
247
|
+
'new-track',
|
|
248
|
+
] );
|
|
249
|
+
} );
|
|
250
|
+
|
|
189
251
|
it( 'adds tracks from the add track control', () => {
|
|
190
252
|
render(
|
|
191
253
|
<PlaylistEdit
|
|
@@ -203,18 +265,7 @@ describe( 'PlaylistEdit', () => {
|
|
|
203
265
|
} )
|
|
204
266
|
);
|
|
205
267
|
|
|
206
|
-
expect(
|
|
207
|
-
expect.objectContaining( { clientId: 'track-1' } ),
|
|
208
|
-
expect.objectContaining( {
|
|
209
|
-
clientId: 'new-track',
|
|
210
|
-
name: 'core/playlist-track',
|
|
211
|
-
attributes: expect.objectContaining( {
|
|
212
|
-
id: 2,
|
|
213
|
-
src: 'https://example.com/second-track.mp3',
|
|
214
|
-
title: 'Second track',
|
|
215
|
-
} ),
|
|
216
|
-
} ),
|
|
217
|
-
] );
|
|
268
|
+
expect( trackClientIds() ).toEqual( [ 'track-1', 'new-track' ] );
|
|
218
269
|
expect( selectBlock ).toHaveBeenCalledWith( 'new-track' );
|
|
219
270
|
} );
|
|
220
271
|
} );
|
|
@@ -21,15 +21,15 @@ _Defined via the [`attributes`](https://developer.wordpress.org/block-editor/ref
|
|
|
21
21
|
| Attribute | [Type](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#type-validation) | [Default](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#default-value) | Description |
|
|
22
22
|
|-----------|------|---------|-------------|
|
|
23
23
|
| `blob` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `local` |
|
|
24
|
-
| `id` | `number` | — |
|
|
25
|
-
| `src` | `string` | — |
|
|
24
|
+
| `id` | `number` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
25
|
+
| `src` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
26
26
|
| `type` | `string` | `"audio"` | — |
|
|
27
|
-
| `album` | `string` | — |
|
|
28
|
-
| `artist` | `string` | — |
|
|
29
|
-
| `image` | `string` | — |
|
|
30
|
-
| `imageAlt` | `string` | — |
|
|
31
|
-
| `length` | `string` | — |
|
|
32
|
-
| `title` | `string` | — |
|
|
27
|
+
| `album` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
28
|
+
| `artist` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
29
|
+
| `image` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
30
|
+
| `imageAlt` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
31
|
+
| `length` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
32
|
+
| `title` | `string` | — | [Role](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/#role): `content` |
|
|
33
33
|
|
|
34
34
|
## Supports
|
|
35
35
|
|
|
@@ -15,32 +15,40 @@
|
|
|
15
15
|
"role": "local"
|
|
16
16
|
},
|
|
17
17
|
"id": {
|
|
18
|
-
"type": "number"
|
|
18
|
+
"type": "number",
|
|
19
|
+
"role": "content"
|
|
19
20
|
},
|
|
20
21
|
"src": {
|
|
21
|
-
"type": "string"
|
|
22
|
+
"type": "string",
|
|
23
|
+
"role": "content"
|
|
22
24
|
},
|
|
23
25
|
"type": {
|
|
24
26
|
"type": "string",
|
|
25
27
|
"default": "audio"
|
|
26
28
|
},
|
|
27
29
|
"album": {
|
|
28
|
-
"type": "string"
|
|
30
|
+
"type": "string",
|
|
31
|
+
"role": "content"
|
|
29
32
|
},
|
|
30
33
|
"artist": {
|
|
31
|
-
"type": "string"
|
|
34
|
+
"type": "string",
|
|
35
|
+
"role": "content"
|
|
32
36
|
},
|
|
33
37
|
"image": {
|
|
34
|
-
"type": "string"
|
|
38
|
+
"type": "string",
|
|
39
|
+
"role": "content"
|
|
35
40
|
},
|
|
36
41
|
"imageAlt": {
|
|
37
|
-
"type": "string"
|
|
42
|
+
"type": "string",
|
|
43
|
+
"role": "content"
|
|
38
44
|
},
|
|
39
45
|
"length": {
|
|
40
|
-
"type": "string"
|
|
46
|
+
"type": "string",
|
|
47
|
+
"role": "content"
|
|
41
48
|
},
|
|
42
49
|
"title": {
|
|
43
|
-
"type": "string"
|
|
50
|
+
"type": "string",
|
|
51
|
+
"role": "content"
|
|
44
52
|
}
|
|
45
53
|
},
|
|
46
54
|
"supports": {
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
Button,
|
|
16
16
|
PanelBody,
|
|
17
17
|
TextControl,
|
|
18
|
-
TextareaControl,
|
|
18
|
+
TextareaControl as WCTextareaControl,
|
|
19
19
|
BaseControl,
|
|
20
20
|
Spinner,
|
|
21
21
|
} from '@wordpress/components';
|
|
@@ -46,11 +46,27 @@ const PlaylistTrackEdit = ( {
|
|
|
46
46
|
const showImages = context?.showImages ?? true;
|
|
47
47
|
const imageButton = useRef();
|
|
48
48
|
const blockProps = useBlockProps();
|
|
49
|
-
const { currentTrackClientId, setCurrentTrackClientId } =
|
|
49
|
+
const { currentTrackClientId, setCurrentTrackClientId, removeTrack } =
|
|
50
50
|
useContext( PlaylistContext );
|
|
51
51
|
const { createErrorNotice } = useDispatch( noticesStore );
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Handle audio upload errors.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} message Error message to show.
|
|
57
|
+
* @param {Object} [options]
|
|
58
|
+
* @param {boolean} [options.removeTrackOnError] Remove the track as well. Useful for drag/drop where a track is optimistically created before upload finishes.
|
|
59
|
+
*/
|
|
60
|
+
function onUploadError( message, { removeTrackOnError = false } = {} ) {
|
|
53
61
|
createErrorNotice( message, { type: 'snackbar' } );
|
|
62
|
+
|
|
63
|
+
if ( removeTrackOnError ) {
|
|
64
|
+
removeTrack( clientId );
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Set temporaryURL back to default state
|
|
69
|
+
setTemporaryURL();
|
|
54
70
|
}
|
|
55
71
|
const hasTrackSource = !! src || !! temporaryURL;
|
|
56
72
|
|
|
@@ -70,11 +86,14 @@ const PlaylistTrackEdit = ( {
|
|
|
70
86
|
setCurrentTrackClientId,
|
|
71
87
|
] );
|
|
72
88
|
|
|
89
|
+
// Handles drag/drop uploads
|
|
73
90
|
useUploadMediaFromBlobURL( {
|
|
74
91
|
url: temporaryURL,
|
|
75
92
|
allowedTypes: ALLOWED_MEDIA_TYPES,
|
|
76
93
|
onChange: onSelectTrack,
|
|
77
|
-
onError:
|
|
94
|
+
onError: ( message ) => {
|
|
95
|
+
onUploadError( message, { removeTrackOnError: true } );
|
|
96
|
+
},
|
|
78
97
|
} );
|
|
79
98
|
|
|
80
99
|
function onSelectTrack( media ) {
|
|
@@ -144,20 +163,33 @@ const PlaylistTrackEdit = ( {
|
|
|
144
163
|
|
|
145
164
|
return (
|
|
146
165
|
<>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
{ /* Only show if this is a single selection (not multiselect) */ }
|
|
167
|
+
{ isSelected && (
|
|
168
|
+
<BlockControls group="other">
|
|
169
|
+
<MediaReplaceFlow
|
|
170
|
+
name={ __( 'Replace' ) }
|
|
171
|
+
onSelect={ onSelectTrack }
|
|
172
|
+
accept="audio/*"
|
|
173
|
+
mediaId={ id }
|
|
174
|
+
mediaURL={ src }
|
|
175
|
+
allowedTypes={ ALLOWED_MEDIA_TYPES }
|
|
176
|
+
onError={ onUploadError }
|
|
177
|
+
variant="toolbar"
|
|
178
|
+
/>
|
|
179
|
+
</BlockControls>
|
|
180
|
+
) }
|
|
159
181
|
<InspectorControls>
|
|
160
182
|
<PanelBody title={ __( 'Settings' ) }>
|
|
183
|
+
{ /* Only show if this is a single selection (not multiselect) */ }
|
|
184
|
+
{ isSelected && (
|
|
185
|
+
<TextControl
|
|
186
|
+
label={ __( 'Title' ) }
|
|
187
|
+
value={ title ? stripHTML( title ) : '' }
|
|
188
|
+
onChange={ ( titleValue ) => {
|
|
189
|
+
setAttributes( { title: titleValue } );
|
|
190
|
+
} }
|
|
191
|
+
/>
|
|
192
|
+
) }
|
|
161
193
|
<TextControl
|
|
162
194
|
label={ __( 'Artist' ) }
|
|
163
195
|
value={ artist ? stripHTML( artist ) : '' }
|
|
@@ -172,13 +204,6 @@ const PlaylistTrackEdit = ( {
|
|
|
172
204
|
setAttributes( { album: albumValue } );
|
|
173
205
|
} }
|
|
174
206
|
/>
|
|
175
|
-
<TextControl
|
|
176
|
-
label={ __( 'Title' ) }
|
|
177
|
-
value={ title ? stripHTML( title ) : '' }
|
|
178
|
-
onChange={ ( titleValue ) => {
|
|
179
|
-
setAttributes( { title: titleValue } );
|
|
180
|
-
} }
|
|
181
|
-
/>
|
|
182
207
|
<MediaUploadCheck>
|
|
183
208
|
<BaseControl>
|
|
184
209
|
<BaseControl.VisualLabel>
|
|
@@ -225,7 +250,7 @@ const PlaylistTrackEdit = ( {
|
|
|
225
250
|
</BaseControl>
|
|
226
251
|
</MediaUploadCheck>
|
|
227
252
|
{ !! image && (
|
|
228
|
-
<
|
|
253
|
+
<WCTextareaControl
|
|
229
254
|
label={ __( 'Alternative text' ) }
|
|
230
255
|
value={ imageAlt || '' }
|
|
231
256
|
onChange={ ( value ) =>
|
|
@@ -251,7 +276,6 @@ const PlaylistTrackEdit = ( {
|
|
|
251
276
|
</PanelBody>
|
|
252
277
|
</InspectorControls>
|
|
253
278
|
<li { ...blockProps }>
|
|
254
|
-
{ !! temporaryURL && <Spinner /> }
|
|
255
279
|
<button
|
|
256
280
|
className="wp-block-playlist-track__button"
|
|
257
281
|
onClick={ () => setCurrentTrackClientId( clientId ) }
|
|
@@ -271,7 +295,8 @@ const PlaylistTrackEdit = ( {
|
|
|
271
295
|
tagName="span"
|
|
272
296
|
className="wp-block-playlist-track__title"
|
|
273
297
|
value={ title }
|
|
274
|
-
|
|
298
|
+
aria-label={ __( 'Track title' ) }
|
|
299
|
+
placeholder={ __( 'Track title' ) }
|
|
275
300
|
onChange={ ( value ) => {
|
|
276
301
|
setAttributes( { title: value } );
|
|
277
302
|
} }
|
|
@@ -282,7 +307,8 @@ const PlaylistTrackEdit = ( {
|
|
|
282
307
|
tagName="span"
|
|
283
308
|
className="wp-block-playlist-track__artist"
|
|
284
309
|
value={ artist }
|
|
285
|
-
|
|
310
|
+
aria-label={ __( 'Track artist' ) }
|
|
311
|
+
placeholder={ __( 'Track artist' ) }
|
|
286
312
|
onChange={ ( value ) =>
|
|
287
313
|
setAttributes( { artist: value } )
|
|
288
314
|
}
|
|
@@ -300,6 +326,9 @@ const PlaylistTrackEdit = ( {
|
|
|
300
326
|
</span>
|
|
301
327
|
) }
|
|
302
328
|
{ length }
|
|
329
|
+
{ !! temporaryURL && (
|
|
330
|
+
<Spinner className="wp-block-playlist-track__spinner" />
|
|
331
|
+
) }
|
|
303
332
|
</span>
|
|
304
333
|
<span className="screen-reader-text">{ __( 'Play' ) }</span>
|
|
305
334
|
</button>
|