@wordpress/block-library 9.12.0 → 9.13.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.
Files changed (145) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/block-keyboard-shortcuts/index.js +7 -7
  3. package/build/block-keyboard-shortcuts/index.js.map +1 -1
  4. package/build/cover/edit/index.js +1 -1
  5. package/build/cover/edit/index.js.map +1 -1
  6. package/build/cover/index.js +10 -10
  7. package/build/cover/index.js.map +1 -1
  8. package/build/cover/shared.js +6 -7
  9. package/build/cover/shared.js.map +1 -1
  10. package/build/details/index.js +19 -0
  11. package/build/details/index.js.map +1 -1
  12. package/build/file/edit.js +7 -3
  13. package/build/file/edit.js.map +1 -1
  14. package/build/freeform/modal.js +1 -1
  15. package/build/freeform/modal.js.map +1 -1
  16. package/build/heading/index.js +1 -7
  17. package/build/heading/index.js.map +1 -1
  18. package/build/home-link/edit.js +20 -36
  19. package/build/home-link/edit.js.map +1 -1
  20. package/build/navigation/use-template-part-area-label.js +6 -8
  21. package/build/navigation/use-template-part-area-label.js.map +1 -1
  22. package/build/page-list/index.js +14 -0
  23. package/build/page-list/index.js.map +1 -1
  24. package/build/paragraph/edit.js +26 -25
  25. package/build/paragraph/edit.js.map +1 -1
  26. package/build/query/edit/inspector-controls/enhanced-pagination-control.js +1 -3
  27. package/build/query/edit/inspector-controls/enhanced-pagination-control.js.map +1 -1
  28. package/build/query/edit/inspector-controls/index.js +1 -11
  29. package/build/query/edit/inspector-controls/index.js.map +1 -1
  30. package/build/query/utils.js +6 -8
  31. package/build/query/utils.js.map +1 -1
  32. package/build/social-link/edit.js +57 -10
  33. package/build/social-link/edit.js.map +1 -1
  34. package/build/social-link/index.js +4 -2
  35. package/build/social-link/index.js.map +1 -1
  36. package/build/template-part/edit/advanced-controls.js +2 -7
  37. package/build/template-part/edit/advanced-controls.js.map +1 -1
  38. package/build/template-part/edit/utils/get-template-part-icon.js +23 -0
  39. package/build/template-part/edit/utils/get-template-part-icon.js.map +1 -0
  40. package/build/template-part/edit/utils/hooks.js +1 -6
  41. package/build/template-part/edit/utils/hooks.js.map +1 -1
  42. package/build/template-part/variations.js +6 -12
  43. package/build/template-part/variations.js.map +1 -1
  44. package/build/video/tracks-editor.js +135 -121
  45. package/build/video/tracks-editor.js.map +1 -1
  46. package/build-module/block-keyboard-shortcuts/index.js +7 -7
  47. package/build-module/block-keyboard-shortcuts/index.js.map +1 -1
  48. package/build-module/cover/edit/index.js +1 -1
  49. package/build-module/cover/edit/index.js.map +1 -1
  50. package/build-module/cover/index.js +10 -10
  51. package/build-module/cover/index.js.map +1 -1
  52. package/build-module/cover/shared.js +6 -7
  53. package/build-module/cover/shared.js.map +1 -1
  54. package/build-module/details/index.js +20 -1
  55. package/build-module/details/index.js.map +1 -1
  56. package/build-module/file/edit.js +7 -3
  57. package/build-module/file/edit.js.map +1 -1
  58. package/build-module/freeform/modal.js +1 -1
  59. package/build-module/freeform/modal.js.map +1 -1
  60. package/build-module/heading/index.js +1 -7
  61. package/build-module/heading/index.js.map +1 -1
  62. package/build-module/home-link/edit.js +23 -39
  63. package/build-module/home-link/edit.js.map +1 -1
  64. package/build-module/navigation/use-template-part-area-label.js +6 -8
  65. package/build-module/navigation/use-template-part-area-label.js.map +1 -1
  66. package/build-module/page-list/index.js +14 -0
  67. package/build-module/page-list/index.js.map +1 -1
  68. package/build-module/paragraph/edit.js +26 -25
  69. package/build-module/paragraph/edit.js.map +1 -1
  70. package/build-module/query/edit/inspector-controls/enhanced-pagination-control.js +1 -3
  71. package/build-module/query/edit/inspector-controls/enhanced-pagination-control.js.map +1 -1
  72. package/build-module/query/edit/inspector-controls/index.js +1 -11
  73. package/build-module/query/edit/inspector-controls/index.js.map +1 -1
  74. package/build-module/query/utils.js +6 -8
  75. package/build-module/query/utils.js.map +1 -1
  76. package/build-module/social-link/edit.js +61 -14
  77. package/build-module/social-link/edit.js.map +1 -1
  78. package/build-module/social-link/index.js +4 -2
  79. package/build-module/social-link/index.js.map +1 -1
  80. package/build-module/template-part/edit/advanced-controls.js +3 -8
  81. package/build-module/template-part/edit/advanced-controls.js.map +1 -1
  82. package/build-module/template-part/edit/utils/get-template-part-icon.js +15 -0
  83. package/build-module/template-part/edit/utils/get-template-part-icon.js.map +1 -0
  84. package/build-module/template-part/edit/utils/hooks.js +1 -6
  85. package/build-module/template-part/edit/utils/hooks.js.map +1 -1
  86. package/build-module/template-part/variations.js +5 -11
  87. package/build-module/template-part/variations.js.map +1 -1
  88. package/build-module/video/tracks-editor.js +136 -122
  89. package/build-module/video/tracks-editor.js.map +1 -1
  90. package/build-style/editor-rtl.css +16 -8
  91. package/build-style/editor.css +16 -8
  92. package/build-style/navigation/style-rtl.css +1 -1
  93. package/build-style/navigation/style.css +1 -1
  94. package/build-style/page-list/style-rtl.css +4 -0
  95. package/build-style/page-list/style.css +4 -0
  96. package/build-style/query/editor-rtl.css +0 -4
  97. package/build-style/query/editor.css +0 -4
  98. package/build-style/search/style-rtl.css +3 -3
  99. package/build-style/search/style.css +3 -3
  100. package/build-style/social-link/editor-rtl.css +8 -4
  101. package/build-style/social-link/editor.css +8 -4
  102. package/build-style/style-rtl.css +8 -4
  103. package/build-style/style.css +8 -4
  104. package/build-style/video/editor-rtl.css +8 -0
  105. package/build-style/video/editor.css +8 -0
  106. package/package.json +2 -2
  107. package/src/block-keyboard-shortcuts/index.js +25 -11
  108. package/src/cover/edit/index.js +1 -1
  109. package/src/cover/index.js +8 -8
  110. package/src/cover/shared.js +10 -10
  111. package/src/cover/test/edit.js +2 -2
  112. package/src/details/index.js +23 -1
  113. package/src/file/edit.js +8 -2
  114. package/src/freeform/modal.js +1 -1
  115. package/src/heading/block.json +1 -7
  116. package/src/home-link/edit.js +27 -45
  117. package/src/home-link/index.php +0 -3
  118. package/src/navigation/index.php +4 -150
  119. package/src/navigation/style.scss +1 -1
  120. package/src/navigation/use-template-part-area-label.js +10 -10
  121. package/src/page-list/block.json +14 -0
  122. package/src/page-list/style.scss +4 -0
  123. package/src/paragraph/edit.js +20 -17
  124. package/src/pattern/index.php +0 -7
  125. package/src/query/edit/inspector-controls/enhanced-pagination-control.js +3 -5
  126. package/src/query/edit/inspector-controls/index.js +0 -10
  127. package/src/query/editor.scss +0 -4
  128. package/src/query/index.php +1 -1
  129. package/src/query/utils.js +14 -15
  130. package/src/rss/index.php +11 -8
  131. package/src/search/style.scss +3 -5
  132. package/src/social-link/block.json +4 -2
  133. package/src/social-link/edit.js +87 -19
  134. package/src/social-link/editor.scss +11 -7
  135. package/src/template-part/edit/advanced-controls.js +13 -13
  136. package/src/template-part/edit/utils/get-template-part-icon.js +20 -0
  137. package/src/template-part/edit/utils/hooks.js +2 -7
  138. package/src/template-part/variations.js +4 -16
  139. package/src/video/editor.scss +9 -0
  140. package/src/video/tracks-editor.js +157 -139
  141. package/build/query/edit/inspector-controls/create-new-post-link.js +0 -40
  142. package/build/query/edit/inspector-controls/create-new-post-link.js.map +0 -1
  143. package/build-module/query/edit/inspector-controls/create-new-post-link.js +0 -33
  144. package/build-module/query/edit/inspector-controls/create-new-post-link.js.map +0 -1
  145. package/src/query/edit/inspector-controls/create-new-post-link.js +0 -32
@@ -6,24 +6,29 @@ import clsx from 'clsx';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { DELETE, BACKSPACE } from '@wordpress/keycodes';
9
+ import { DELETE, BACKSPACE, ENTER } from '@wordpress/keycodes';
10
10
  import { useDispatch } from '@wordpress/data';
11
11
 
12
12
  import {
13
+ BlockControls,
13
14
  InspectorControls,
14
15
  URLPopover,
15
16
  URLInput,
17
+ useBlockEditingMode,
16
18
  useBlockProps,
17
19
  store as blockEditorStore,
18
20
  } from '@wordpress/block-editor';
19
- import { useState } from '@wordpress/element';
21
+ import { useState, useRef } from '@wordpress/element';
20
22
  import {
21
23
  Button,
24
+ Dropdown,
22
25
  PanelBody,
23
26
  PanelRow,
24
27
  TextControl,
28
+ ToolbarButton,
25
29
  __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper,
26
30
  } from '@wordpress/components';
31
+ import { useMergeRefs } from '@wordpress/compose';
27
32
  import { __ } from '@wordpress/i18n';
28
33
  import { keyboardReturn } from '@wordpress/icons';
29
34
 
@@ -112,16 +117,24 @@ const SocialLinkEdit = ( {
112
117
  iconBackgroundColorValue,
113
118
  } = context;
114
119
  const [ showURLPopover, setPopover ] = useState( false );
115
- const classes = clsx( 'wp-social-link', 'wp-social-link-' + service, {
116
- 'wp-social-link__is-incomplete': ! url,
117
- [ `has-${ iconColor }-color` ]: iconColor,
118
- [ `has-${ iconBackgroundColor }-background-color` ]:
119
- iconBackgroundColor,
120
- } );
120
+ const wrapperClasses = clsx(
121
+ 'wp-social-link',
122
+ // Manually adding this class for backwards compatibility of CSS when moving the
123
+ // blockProps from the li to the button: https://github.com/WordPress/gutenberg/pull/64883
124
+ 'wp-block-social-link',
125
+ 'wp-social-link-' + service,
126
+ {
127
+ 'wp-social-link__is-incomplete': ! url,
128
+ [ `has-${ iconColor }-color` ]: iconColor,
129
+ [ `has-${ iconBackgroundColor }-background-color` ]:
130
+ iconBackgroundColor,
131
+ }
132
+ );
121
133
 
122
134
  // Use internal state instead of a ref to make sure that the component
123
135
  // re-renders when the popover's anchor updates.
124
136
  const [ popoverAnchor, setPopoverAnchor ] = useState( null );
137
+ const isContentOnlyMode = useBlockEditingMode() === 'contentOnly';
125
138
 
126
139
  const IconComponent = getIconBySite( service );
127
140
  const socialLinkName = getNameBySite( service );
@@ -131,16 +144,56 @@ const SocialLinkEdit = ( {
131
144
  // spaces. The PHP render callback fallbacks to the social name as well.
132
145
  const socialLinkText = label.trim() === '' ? socialLinkName : label;
133
146
 
147
+ const ref = useRef();
134
148
  const blockProps = useBlockProps( {
135
- className: classes,
136
- style: {
137
- color: iconColorValue,
138
- backgroundColor: iconBackgroundColorValue,
149
+ className: 'wp-block-social-link-anchor',
150
+ ref: useMergeRefs( [ setPopoverAnchor, ref ] ),
151
+ onClick: () => setPopover( true ),
152
+ onKeyDown: ( event ) => {
153
+ if ( event.keyCode === ENTER ) {
154
+ event.preventDefault();
155
+ setPopover( true );
156
+ }
139
157
  },
140
158
  } );
141
159
 
142
160
  return (
143
161
  <>
162
+ { isContentOnlyMode && showLabels && (
163
+ // Add an extra control to modify the label attribute when content only mode is active.
164
+ // With content only mode active, the inspector is hidden, so users need another way
165
+ // to edit this attribute.
166
+ <BlockControls group="other">
167
+ <Dropdown
168
+ popoverProps={ { position: 'bottom right' } }
169
+ renderToggle={ ( { isOpen, onToggle } ) => (
170
+ <ToolbarButton
171
+ onClick={ onToggle }
172
+ aria-haspopup="true"
173
+ aria-expanded={ isOpen }
174
+ >
175
+ { __( 'Text' ) }
176
+ </ToolbarButton>
177
+ ) }
178
+ renderContent={ () => (
179
+ <TextControl
180
+ __next40pxDefaultSize
181
+ __nextHasNoMarginBottom
182
+ className="wp-block-social-link__toolbar_content_text"
183
+ label={ __( 'Text' ) }
184
+ help={ __(
185
+ 'Provide a text label or use the default.'
186
+ ) }
187
+ value={ label }
188
+ onChange={ ( value ) =>
189
+ setAttributes( { label: value } )
190
+ }
191
+ placeholder={ socialLinkName }
192
+ />
193
+ ) }
194
+ />
195
+ </BlockControls>
196
+ ) }
144
197
  <InspectorControls>
145
198
  <PanelBody title={ __( 'Settings' ) }>
146
199
  <PanelRow>
@@ -169,13 +222,27 @@ const SocialLinkEdit = ( {
169
222
  onChange={ ( value ) => setAttributes( { rel: value } ) }
170
223
  />
171
224
  </InspectorControls>
172
- <li { ...blockProps }>
173
- <button
174
- className="wp-block-social-link-anchor"
175
- ref={ setPopoverAnchor }
176
- onClick={ () => setPopover( true ) }
177
- aria-haspopup="dialog"
178
- >
225
+ { /*
226
+ * Because the `<ul>` element has a role=document, the `<li>` is
227
+ * not semantically correct, so adding role=presentation is cleaner.
228
+ * https://github.com/WordPress/gutenberg/pull/64883#issuecomment-2472874551
229
+ */ }
230
+ <li
231
+ role="presentation"
232
+ className={ wrapperClasses }
233
+ style={ {
234
+ color: iconColorValue,
235
+ backgroundColor: iconBackgroundColorValue,
236
+ } }
237
+ >
238
+ { /*
239
+ * Disable reason: The `button` ARIA role is redundant but
240
+ * blockProps has a role of `document` automatically applied
241
+ * which breaks the semantics of this button since it removes
242
+ * the information about the popover.
243
+ */
244
+ /* eslint-disable jsx-a11y/no-redundant-roles */ }
245
+ <button aria-haspopup="dialog" { ...blockProps } role="button">
179
246
  <IconComponent />
180
247
  <span
181
248
  className={ clsx( 'wp-block-social-link-label', {
@@ -185,6 +252,7 @@ const SocialLinkEdit = ( {
185
252
  { socialLinkText }
186
253
  </span>
187
254
  </button>
255
+ { /* eslint-enable jsx-a11y/no-redundant-roles */ }
188
256
  { isSelected && showURLPopover && (
189
257
  <SocialLinkURLPopover
190
258
  url={ url }
@@ -14,6 +14,8 @@
14
14
  font-size: inherit;
15
15
  color: currentColor;
16
16
  height: auto;
17
+ font-weight: inherit;
18
+ font-family: inherit;
17
19
 
18
20
  // This rule ensures social link buttons display correctly in template parts.
19
21
  opacity: 1;
@@ -21,13 +23,10 @@
21
23
  // This rule is duplicated from the style.scss and needs to be the same as there.
22
24
  padding: 0.25em;
23
25
 
24
- // Focus styles replicate the `@wordpress/components` button component.
25
- &:focus:not(:disabled) {
26
- border-radius: 2px;
27
- box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
28
-
29
- // Windows High Contrast mode will show this outline, but not the box-shadow.
30
- outline: 3px solid transparent;
26
+ // Override the shared `.wp-block-social-link` class used on both the li and button
27
+ // due to backwards compatibility from moving the blockProps from the li to the button.
28
+ &:hover {
29
+ transform: none;
31
30
  }
32
31
  }
33
32
 
@@ -39,3 +38,8 @@
39
38
  :root :where(.wp-block-social-links.is-style-logos-only .wp-social-link button) {
40
39
  padding: 0;
41
40
  }
41
+
42
+ .wp-block-social-link__toolbar_content_text {
43
+ // Corresponds to the size of the text control input in the block inspector.
44
+ width: 250px;
45
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useEntityProp } from '@wordpress/core-data';
4
+ import { useEntityProp, store as coreStore } from '@wordpress/core-data';
5
5
  import { SelectControl, TextControl } from '@wordpress/components';
6
6
  import { sprintf, __ } from '@wordpress/i18n';
7
7
  import { useSelect } from '@wordpress/data';
@@ -54,19 +54,19 @@ export function TemplatePartAdvancedControls( {
54
54
  templatePartId
55
55
  );
56
56
 
57
- const definedAreas = useSelect( ( select ) => {
58
- // FIXME: @wordpress/block-library should not depend on @wordpress/editor.
59
- // Blocks can be loaded into a *non-post* block editor.
60
- /* eslint-disable-next-line @wordpress/data-no-store-string-literals */
61
- return select(
62
- 'core/editor'
63
- ).__experimentalGetDefaultTemplatePartAreas();
64
- }, [] );
57
+ const defaultTemplatePartAreas = useSelect(
58
+ ( select ) =>
59
+ select( coreStore ).getEntityRecord( 'root', '__unstableBase' )
60
+ ?.default_template_part_areas || [],
61
+ []
62
+ );
65
63
 
66
- const areaOptions = definedAreas.map( ( { label, area: _area } ) => ( {
67
- label,
68
- value: _area,
69
- } ) );
64
+ const areaOptions = defaultTemplatePartAreas.map(
65
+ ( { label, area: _area } ) => ( {
66
+ label,
67
+ value: _area,
68
+ } )
69
+ );
70
70
 
71
71
  return (
72
72
  <>
@@ -0,0 +1,20 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ header as headerIcon,
6
+ footer as footerIcon,
7
+ sidebar as sidebarIcon,
8
+ symbolFilled as symbolFilledIcon,
9
+ } from '@wordpress/icons';
10
+
11
+ export const getTemplatePartIcon = ( iconName ) => {
12
+ if ( 'header' === iconName ) {
13
+ return headerIcon;
14
+ } else if ( 'footer' === iconName ) {
15
+ return footerIcon;
16
+ } else if ( 'sidebar' === iconName ) {
17
+ return sidebarIcon;
18
+ }
19
+ return symbolFilledIcon;
20
+ };
@@ -136,14 +136,9 @@ export function useCreateTemplatePartFromBlocks( area, setAttributes ) {
136
136
  export function useTemplatePartArea( area ) {
137
137
  return useSelect(
138
138
  ( select ) => {
139
- // FIXME: @wordpress/block-library should not depend on @wordpress/editor.
140
- // Blocks can be loaded into a *non-post* block editor.
141
- /* eslint-disable @wordpress/data-no-store-string-literals */
142
139
  const definedAreas =
143
- select(
144
- 'core/editor'
145
- ).__experimentalGetDefaultTemplatePartAreas();
146
- /* eslint-enable @wordpress/data-no-store-string-literals */
140
+ select( coreStore ).getEntityRecord( 'root', '__unstableBase' )
141
+ ?.default_template_part_areas || [];
147
142
 
148
143
  const selectedArea = definedAreas.find(
149
144
  ( definedArea ) => definedArea.area === area
@@ -3,23 +3,11 @@
3
3
  */
4
4
  import { store as coreDataStore } from '@wordpress/core-data';
5
5
  import { select } from '@wordpress/data';
6
- import {
7
- header as headerIcon,
8
- footer as footerIcon,
9
- sidebar as sidebarIcon,
10
- symbolFilled as symbolFilledIcon,
11
- } from '@wordpress/icons';
12
6
 
13
- function getTemplatePartIcon( iconName ) {
14
- if ( 'header' === iconName ) {
15
- return headerIcon;
16
- } else if ( 'footer' === iconName ) {
17
- return footerIcon;
18
- } else if ( 'sidebar' === iconName ) {
19
- return sidebarIcon;
20
- }
21
- return symbolFilledIcon;
22
- }
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { getTemplatePartIcon } from './edit/utils/get-template-part-icon';
23
11
 
24
12
  export function enhanceTemplatePartVariations( settings, name ) {
25
13
  if ( name !== 'core/template-part' ) {
@@ -37,6 +37,7 @@
37
37
  max-width: 240px;
38
38
  }
39
39
 
40
+ .block-library-video-tracks-editor__tracks-informative-message-title,
40
41
  .block-library-video-tracks-editor__single-track-editor-edit-track-label {
41
42
  margin-top: $grid-unit-05;
42
43
  color: $gray-700;
@@ -56,3 +57,11 @@
56
57
  padding: 0;
57
58
  }
58
59
  }
60
+
61
+ .block-library-video-tracks-editor__tracks-informative-message {
62
+ padding: $grid-unit-10;
63
+
64
+ &-description {
65
+ margin-bottom: 0;
66
+ }
67
+ }
@@ -24,7 +24,7 @@ import {
24
24
  } from '@wordpress/block-editor';
25
25
  import { upload, media } from '@wordpress/icons';
26
26
  import { useSelect } from '@wordpress/data';
27
- import { useState } from '@wordpress/element';
27
+ import { useState, useRef, useEffect } from '@wordpress/element';
28
28
  import { getFilename } from '@wordpress/url';
29
29
 
30
30
  const ALLOWED_TYPES = [ 'text/vtt' ];
@@ -40,39 +40,29 @@ const KIND_OPTIONS = [
40
40
  ];
41
41
 
42
42
  function TrackList( { tracks, onEditPress } ) {
43
- let content;
44
- if ( tracks.length === 0 ) {
45
- content = (
46
- <p className="block-library-video-tracks-editor__tracks-informative-message">
47
- { __(
48
- 'Tracks can be subtitles, captions, chapters, or descriptions. They help make your content more accessible to a wider range of users.'
49
- ) }
50
- </p>
51
- );
52
- } else {
53
- content = tracks.map( ( track, index ) => {
54
- return (
55
- <HStack
56
- key={ index }
57
- className="block-library-video-tracks-editor__track-list-track"
43
+ const content = tracks.map( ( track, index ) => {
44
+ return (
45
+ <HStack
46
+ key={ index }
47
+ className="block-library-video-tracks-editor__track-list-track"
48
+ >
49
+ <span>{ track.label }</span>
50
+ <Button
51
+ __next40pxDefaultSize
52
+ variant="tertiary"
53
+ onClick={ () => onEditPress( index ) }
54
+ aria-label={ sprintf(
55
+ /* translators: %s: Label of the video text track e.g: "French subtitles". */
56
+ _x( 'Edit %s', 'text tracks' ),
57
+ track.label
58
+ ) }
58
59
  >
59
- <span>{ track.label } </span>
60
- <Button
61
- __next40pxDefaultSize
62
- variant="tertiary"
63
- onClick={ () => onEditPress( index ) }
64
- aria-label={ sprintf(
65
- /* translators: %s: Label of the video text track e.g: "French subtitles" */
66
- _x( 'Edit %s', 'text tracks' ),
67
- track.label
68
- ) }
69
- >
70
- { __( 'Edit' ) }
71
- </Button>
72
- </HStack>
73
- );
74
- } );
75
- }
60
+ { __( 'Edit' ) }
61
+ </Button>
62
+ </HStack>
63
+ );
64
+ } );
65
+
76
66
  return (
77
67
  <MenuGroup
78
68
  label={ __( 'Text tracks' ) }
@@ -87,105 +77,100 @@ function SingleTrackEditor( { track, onChange, onClose, onRemove } ) {
87
77
  const { src = '', label = '', srcLang = '', kind = DEFAULT_KIND } = track;
88
78
  const fileName = src.startsWith( 'blob:' ) ? '' : getFilename( src ) || '';
89
79
  return (
90
- <NavigableMenu>
91
- <VStack
92
- className="block-library-video-tracks-editor__single-track-editor"
93
- spacing="4"
94
- >
95
- <span className="block-library-video-tracks-editor__single-track-editor-edit-track-label">
96
- { __( 'Edit track' ) }
97
- </span>
98
- <span>
99
- { __( 'File' ) }: <b>{ fileName }</b>
100
- </span>
101
- <Grid columns={ 2 } gap={ 4 }>
102
- <TextControl
103
- __next40pxDefaultSize
104
- __nextHasNoMarginBottom
105
- /* eslint-disable jsx-a11y/no-autofocus */
106
- autoFocus
107
- /* eslint-enable jsx-a11y/no-autofocus */
108
- onChange={ ( newLabel ) =>
109
- onChange( {
110
- ...track,
111
- label: newLabel,
112
- } )
113
- }
114
- label={ __( 'Label' ) }
115
- value={ label }
116
- help={ __( 'Title of track' ) }
117
- />
118
- <TextControl
80
+ <VStack
81
+ className="block-library-video-tracks-editor__single-track-editor"
82
+ spacing="4"
83
+ >
84
+ <span className="block-library-video-tracks-editor__single-track-editor-edit-track-label">
85
+ { __( 'Edit track' ) }
86
+ </span>
87
+ <span>
88
+ { __( 'File' ) }: <b>{ fileName }</b>
89
+ </span>
90
+ <Grid columns={ 2 } gap={ 4 }>
91
+ <TextControl
92
+ __next40pxDefaultSize
93
+ __nextHasNoMarginBottom
94
+ onChange={ ( newLabel ) =>
95
+ onChange( {
96
+ ...track,
97
+ label: newLabel,
98
+ } )
99
+ }
100
+ label={ __( 'Label' ) }
101
+ value={ label }
102
+ help={ __( 'Title of track' ) }
103
+ />
104
+ <TextControl
105
+ __next40pxDefaultSize
106
+ __nextHasNoMarginBottom
107
+ onChange={ ( newSrcLang ) =>
108
+ onChange( {
109
+ ...track,
110
+ srcLang: newSrcLang,
111
+ } )
112
+ }
113
+ label={ __( 'Source language' ) }
114
+ value={ srcLang }
115
+ help={ __( 'Language tag (en, fr, etc.)' ) }
116
+ />
117
+ </Grid>
118
+ <VStack spacing="8">
119
+ <SelectControl
120
+ __next40pxDefaultSize
121
+ __nextHasNoMarginBottom
122
+ className="block-library-video-tracks-editor__single-track-editor-kind-select"
123
+ options={ KIND_OPTIONS }
124
+ value={ kind }
125
+ label={ __( 'Kind' ) }
126
+ onChange={ ( newKind ) => {
127
+ onChange( {
128
+ ...track,
129
+ kind: newKind,
130
+ } );
131
+ } }
132
+ />
133
+ <HStack className="block-library-video-tracks-editor__single-track-editor-buttons-container">
134
+ <Button
119
135
  __next40pxDefaultSize
120
- __nextHasNoMarginBottom
121
- onChange={ ( newSrcLang ) =>
122
- onChange( {
123
- ...track,
124
- srcLang: newSrcLang,
125
- } )
126
- }
127
- label={ __( 'Source language' ) }
128
- value={ srcLang }
129
- help={ __( 'Language tag (en, fr, etc.)' ) }
130
- />
131
- </Grid>
132
- <VStack spacing="8">
133
- <SelectControl
136
+ isDestructive
137
+ variant="link"
138
+ onClick={ onRemove }
139
+ >
140
+ { __( 'Remove track' ) }
141
+ </Button>
142
+ <Button
134
143
  __next40pxDefaultSize
135
- __nextHasNoMarginBottom
136
- className="block-library-video-tracks-editor__single-track-editor-kind-select"
137
- options={ KIND_OPTIONS }
138
- value={ kind }
139
- label={ __( 'Kind' ) }
140
- onChange={ ( newKind ) => {
141
- onChange( {
142
- ...track,
143
- kind: newKind,
144
- } );
144
+ variant="primary"
145
+ onClick={ () => {
146
+ const changes = {};
147
+ let hasChanges = false;
148
+ if ( label === '' ) {
149
+ changes.label = __( 'English' );
150
+ hasChanges = true;
151
+ }
152
+ if ( srcLang === '' ) {
153
+ changes.srcLang = 'en';
154
+ hasChanges = true;
155
+ }
156
+ if ( track.kind === undefined ) {
157
+ changes.kind = DEFAULT_KIND;
158
+ hasChanges = true;
159
+ }
160
+ if ( hasChanges ) {
161
+ onChange( {
162
+ ...track,
163
+ ...changes,
164
+ } );
165
+ }
166
+ onClose();
145
167
  } }
146
- />
147
- <HStack className="block-library-video-tracks-editor__single-track-editor-buttons-container">
148
- <Button
149
- __next40pxDefaultSize
150
- variant="secondary"
151
- onClick={ () => {
152
- const changes = {};
153
- let hasChanges = false;
154
- if ( label === '' ) {
155
- changes.label = __( 'English' );
156
- hasChanges = true;
157
- }
158
- if ( srcLang === '' ) {
159
- changes.srcLang = 'en';
160
- hasChanges = true;
161
- }
162
- if ( track.kind === undefined ) {
163
- changes.kind = DEFAULT_KIND;
164
- hasChanges = true;
165
- }
166
- if ( hasChanges ) {
167
- onChange( {
168
- ...track,
169
- ...changes,
170
- } );
171
- }
172
- onClose();
173
- } }
174
- >
175
- { __( 'Close' ) }
176
- </Button>
177
- <Button
178
- __next40pxDefaultSize
179
- isDestructive
180
- variant="link"
181
- onClick={ onRemove }
182
- >
183
- { __( 'Remove track' ) }
184
- </Button>
185
- </HStack>
186
- </VStack>
168
+ >
169
+ { __( 'Apply' ) }
170
+ </Button>
171
+ </HStack>
187
172
  </VStack>
188
- </NavigableMenu>
173
+ </VStack>
189
174
  );
190
175
  }
191
176
 
@@ -194,6 +179,11 @@ export default function TracksEditor( { tracks = [], onChange } ) {
194
179
  return select( blockEditorStore ).getSettings().mediaUpload;
195
180
  }, [] );
196
181
  const [ trackBeingEdited, setTrackBeingEdited ] = useState( null );
182
+ const dropdownPopoverRef = useRef();
183
+
184
+ useEffect( () => {
185
+ dropdownPopoverRef.current?.focus();
186
+ }, [ trackBeingEdited ] );
197
187
 
198
188
  if ( ! mediaUpload ) {
199
189
  return null;
@@ -201,17 +191,32 @@ export default function TracksEditor( { tracks = [], onChange } ) {
201
191
  return (
202
192
  <Dropdown
203
193
  contentClassName="block-library-video-tracks-editor"
204
- renderToggle={ ( { isOpen, onToggle } ) => (
205
- <ToolbarGroup>
206
- <ToolbarButton
207
- aria-expanded={ isOpen }
208
- aria-haspopup="true"
209
- onClick={ onToggle }
210
- >
211
- { __( 'Text tracks' ) }
212
- </ToolbarButton>
213
- </ToolbarGroup>
214
- ) }
194
+ focusOnMount
195
+ popoverProps={ {
196
+ ref: dropdownPopoverRef,
197
+ } }
198
+ renderToggle={ ( { isOpen, onToggle } ) => {
199
+ const handleOnToggle = () => {
200
+ if ( ! isOpen ) {
201
+ // When the Popover opens make sure the initial view is
202
+ // always the track list rather than the edit track UI.
203
+ setTrackBeingEdited( null );
204
+ }
205
+ onToggle();
206
+ };
207
+
208
+ return (
209
+ <ToolbarGroup>
210
+ <ToolbarButton
211
+ aria-expanded={ isOpen }
212
+ aria-haspopup="true"
213
+ onClick={ handleOnToggle }
214
+ >
215
+ { __( 'Text tracks' ) }
216
+ </ToolbarButton>
217
+ </ToolbarGroup>
218
+ );
219
+ } }
215
220
  renderContent={ () => {
216
221
  if ( trackBeingEdited !== null ) {
217
222
  return (
@@ -235,8 +240,21 @@ export default function TracksEditor( { tracks = [], onChange } ) {
235
240
  />
236
241
  );
237
242
  }
243
+
238
244
  return (
239
245
  <>
246
+ { tracks.length === 0 && (
247
+ <div className="block-library-video-tracks-editor__tracks-informative-message">
248
+ <h2 className="block-library-video-tracks-editor__tracks-informative-message-title">
249
+ { __( 'Text tracks' ) }
250
+ </h2>
251
+ <p className="block-library-video-tracks-editor__tracks-informative-message-description">
252
+ { __(
253
+ 'Tracks can be subtitles, captions, chapters, or descriptions. They help make your content more accessible to a wider range of users.'
254
+ ) }
255
+ </p>
256
+ </div>
257
+ ) }
240
258
  <NavigableMenu>
241
259
  <TrackList
242
260
  tracks={ tracks }