@wordpress/block-library 7.18.0 → 7.19.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 (188) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/button/deprecated.js +16 -2
  3. package/build/button/deprecated.js.map +1 -1
  4. package/build/button/edit.js +13 -7
  5. package/build/button/edit.js.map +1 -1
  6. package/build/button/index.js +4 -1
  7. package/build/button/index.js.map +1 -1
  8. package/build/button/save.js +2 -0
  9. package/build/button/save.js.map +1 -1
  10. package/build/columns/transforms.js +5 -0
  11. package/build/columns/transforms.js.map +1 -1
  12. package/build/cover/edit/index.js +3 -1
  13. package/build/cover/edit/index.js.map +1 -1
  14. package/build/gallery/v1/edit.js +4 -2
  15. package/build/gallery/v1/edit.js.map +1 -1
  16. package/build/group/edit.js +98 -45
  17. package/build/group/edit.js.map +1 -1
  18. package/build/group/index.js +3 -0
  19. package/build/group/index.js.map +1 -1
  20. package/build/group/placeholder.js +168 -0
  21. package/build/group/placeholder.js.map +1 -0
  22. package/build/group/variations.js +3 -3
  23. package/build/group/variations.js.map +1 -1
  24. package/build/latest-comments/edit.js +2 -1
  25. package/build/latest-comments/edit.js.map +1 -1
  26. package/build/latest-comments/index.js +5 -1
  27. package/build/latest-comments/index.js.map +1 -1
  28. package/build/latest-posts/deprecated.js +13 -0
  29. package/build/latest-posts/deprecated.js.map +1 -1
  30. package/build/latest-posts/index.js +13 -0
  31. package/build/latest-posts/index.js.map +1 -1
  32. package/build/list-item/edit.js +2 -1
  33. package/build/list-item/edit.js.map +1 -1
  34. package/build/navigation/edit/index.js +82 -92
  35. package/build/navigation/edit/index.js.map +1 -1
  36. package/build/navigation/edit/manage-menus-button.js +36 -0
  37. package/build/navigation/edit/manage-menus-button.js.map +1 -0
  38. package/build/navigation/edit/navigation-menu-selector.js +12 -5
  39. package/build/navigation/edit/navigation-menu-selector.js.map +1 -1
  40. package/build/navigation/edit/responsive-wrapper.js +2 -6
  41. package/build/navigation/edit/responsive-wrapper.js.map +1 -1
  42. package/build/navigation/edit/unsaved-inner-blocks.js +5 -19
  43. package/build/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  44. package/build/navigation-link/edit.js +10 -1
  45. package/build/navigation-link/edit.js.map +1 -1
  46. package/build/post-author/edit.js +35 -20
  47. package/build/post-author/edit.js.map +1 -1
  48. package/build/post-content/index.js +3 -0
  49. package/build/post-content/index.js.map +1 -1
  50. package/build/site-logo/edit.js +1 -1
  51. package/build/site-logo/edit.js.map +1 -1
  52. package/build/template-part/variations.js +5 -0
  53. package/build/template-part/variations.js.map +1 -1
  54. package/build-module/button/deprecated.js +16 -2
  55. package/build-module/button/deprecated.js.map +1 -1
  56. package/build-module/button/edit.js +15 -9
  57. package/build-module/button/edit.js.map +1 -1
  58. package/build-module/button/index.js +4 -1
  59. package/build-module/button/index.js.map +1 -1
  60. package/build-module/button/save.js +2 -0
  61. package/build-module/button/save.js.map +1 -1
  62. package/build-module/columns/transforms.js +5 -0
  63. package/build-module/columns/transforms.js.map +1 -1
  64. package/build-module/cover/edit/index.js +3 -1
  65. package/build-module/cover/edit/index.js.map +1 -1
  66. package/build-module/gallery/v1/edit.js +5 -3
  67. package/build-module/gallery/v1/edit.js.map +1 -1
  68. package/build-module/group/edit.js +94 -45
  69. package/build-module/group/edit.js.map +1 -1
  70. package/build-module/group/index.js +3 -0
  71. package/build-module/group/index.js.map +1 -1
  72. package/build-module/group/placeholder.js +154 -0
  73. package/build-module/group/placeholder.js.map +1 -0
  74. package/build-module/group/variations.js +3 -3
  75. package/build-module/group/variations.js.map +1 -1
  76. package/build-module/latest-comments/edit.js +2 -1
  77. package/build-module/latest-comments/edit.js.map +1 -1
  78. package/build-module/latest-comments/index.js +5 -1
  79. package/build-module/latest-comments/index.js.map +1 -1
  80. package/build-module/latest-posts/deprecated.js +13 -0
  81. package/build-module/latest-posts/deprecated.js.map +1 -1
  82. package/build-module/latest-posts/index.js +13 -0
  83. package/build-module/latest-posts/index.js.map +1 -1
  84. package/build-module/list-item/edit.js +2 -1
  85. package/build-module/list-item/edit.js.map +1 -1
  86. package/build-module/navigation/edit/index.js +84 -94
  87. package/build-module/navigation/edit/index.js.map +1 -1
  88. package/build-module/navigation/edit/manage-menus-button.js +26 -0
  89. package/build-module/navigation/edit/manage-menus-button.js.map +1 -0
  90. package/build-module/navigation/edit/navigation-menu-selector.js +13 -6
  91. package/build-module/navigation/edit/navigation-menu-selector.js.map +1 -1
  92. package/build-module/navigation/edit/responsive-wrapper.js +2 -6
  93. package/build-module/navigation/edit/responsive-wrapper.js.map +1 -1
  94. package/build-module/navigation/edit/unsaved-inner-blocks.js +7 -20
  95. package/build-module/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  96. package/build-module/navigation-link/edit.js +10 -1
  97. package/build-module/navigation-link/edit.js.map +1 -1
  98. package/build-module/post-author/edit.js +35 -21
  99. package/build-module/post-author/edit.js.map +1 -1
  100. package/build-module/post-content/index.js +3 -0
  101. package/build-module/post-content/index.js.map +1 -1
  102. package/build-module/site-logo/edit.js +1 -1
  103. package/build-module/site-logo/edit.js.map +1 -1
  104. package/build-module/template-part/variations.js +5 -0
  105. package/build-module/template-part/variations.js.map +1 -1
  106. package/build-style/editor-rtl.css +76 -1
  107. package/build-style/editor.css +76 -1
  108. package/build-style/group/editor-rtl.css +44 -0
  109. package/build-style/group/editor.css +44 -0
  110. package/build-style/latest-comments/style-rtl.css +1 -0
  111. package/build-style/latest-comments/style.css +1 -0
  112. package/build-style/latest-posts/style-rtl.css +3 -0
  113. package/build-style/latest-posts/style.css +3 -0
  114. package/build-style/navigation/editor-rtl.css +23 -0
  115. package/build-style/navigation/editor.css +23 -0
  116. package/build-style/navigation/style-rtl.css +10 -0
  117. package/build-style/navigation/style.css +10 -0
  118. package/build-style/navigation-link/editor-rtl.css +8 -1
  119. package/build-style/navigation-link/editor.css +8 -1
  120. package/build-style/query/editor-rtl.css +1 -1
  121. package/build-style/query/editor.css +1 -1
  122. package/build-style/query-pagination/style-rtl.css +1 -1
  123. package/build-style/query-pagination/style.css +1 -1
  124. package/build-style/style-rtl.css +20 -1
  125. package/build-style/style.css +20 -1
  126. package/build-style/table/editor-rtl.css +1 -0
  127. package/build-style/table/editor.css +1 -0
  128. package/build-style/table/style-rtl.css +5 -0
  129. package/build-style/table/style.css +5 -0
  130. package/build-style/table/theme-rtl.css +1 -3
  131. package/build-style/table/theme.css +1 -3
  132. package/build-style/theme-rtl.css +1 -3
  133. package/build-style/theme.css +1 -3
  134. package/package.json +28 -28
  135. package/src/avatar/index.php +1 -1
  136. package/src/block/test/edit.native.js +8 -8
  137. package/src/button/block.json +4 -1
  138. package/src/button/deprecated.js +18 -2
  139. package/src/button/edit.js +11 -9
  140. package/src/button/save.js +12 -2
  141. package/src/buttons/test/edit.native.js +19 -19
  142. package/src/columns/test/edit.native.js +32 -32
  143. package/src/columns/transforms.js +8 -0
  144. package/src/cover/edit/index.js +3 -1
  145. package/src/cover/test/edit.native.js +26 -26
  146. package/src/embed/test/index.native.js +43 -43
  147. package/src/gallery/test/index.native.js +11 -11
  148. package/src/gallery/v1/edit.js +19 -24
  149. package/src/group/block.json +3 -0
  150. package/src/group/edit.js +95 -44
  151. package/src/group/editor.scss +48 -0
  152. package/src/group/placeholder.js +187 -0
  153. package/src/group/test/edit.native.js +3 -3
  154. package/src/group/test/placeholder.js +78 -0
  155. package/src/group/variations.js +3 -3
  156. package/src/image/test/edit.native.js +17 -17
  157. package/src/latest-comments/block.json +5 -1
  158. package/src/latest-comments/edit.js +1 -0
  159. package/src/latest-comments/style.scss +3 -0
  160. package/src/latest-posts/block.json +13 -0
  161. package/src/latest-posts/style.scss +3 -0
  162. package/src/list/test/edit.native.js +36 -36
  163. package/src/list-item/edit.js +1 -0
  164. package/src/missing/test/edit-integration.native.js +5 -5
  165. package/src/navigation/edit/index.js +173 -146
  166. package/src/navigation/edit/manage-menus-button.js +21 -0
  167. package/src/navigation/edit/navigation-menu-selector.js +20 -5
  168. package/src/navigation/edit/responsive-wrapper.js +2 -10
  169. package/src/navigation/edit/unsaved-inner-blocks.js +5 -29
  170. package/src/navigation/editor.scss +25 -0
  171. package/src/navigation/style.scss +16 -0
  172. package/src/navigation-link/edit.js +8 -0
  173. package/src/navigation-link/editor.scss +8 -0
  174. package/src/post-author/edit.js +44 -20
  175. package/src/post-content/block.json +3 -0
  176. package/src/query/editor.scss +1 -1
  177. package/src/query-pagination/style.scss +1 -1
  178. package/src/read-more/index.php +9 -2
  179. package/src/shortcode/test/edit.native.js +5 -5
  180. package/src/site-logo/edit.js +1 -1
  181. package/src/social-link/test/index.native.js +10 -10
  182. package/src/social-links/test/edit.native.js +4 -4
  183. package/src/spacer/test/index.native.js +17 -17
  184. package/src/table/editor.scss +1 -0
  185. package/src/table/style.scss +7 -0
  186. package/src/table/theme.scss +1 -3
  187. package/src/template-part/index.php +5 -0
  188. package/src/template-part/variations.js +4 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { filter, find, get, isEmpty, map, reduce } from 'lodash';
4
+ import { filter, find, get, isEmpty, map } from 'lodash';
5
5
 
6
6
  /**
7
7
  * WordPress dependencies
@@ -98,34 +98,29 @@ function GalleryEdit( props ) {
98
98
 
99
99
  const resizedImages = useMemo( () => {
100
100
  if ( isSelected ) {
101
- return reduce(
102
- attributes.ids,
101
+ return ( attributes.ids ?? [] ).reduce(
103
102
  ( currentResizedImages, id ) => {
104
103
  if ( ! id ) {
105
104
  return currentResizedImages;
106
105
  }
107
106
  const image = getMedia( id );
108
- const sizes = reduce(
109
- imageSizes,
110
- ( currentSizes, size ) => {
111
- const defaultUrl = get( image, [
112
- 'sizes',
113
- size.slug,
114
- 'url',
115
- ] );
116
- const mediaDetailsUrl = get( image, [
117
- 'media_details',
118
- 'sizes',
119
- size.slug,
120
- 'source_url',
121
- ] );
122
- return {
123
- ...currentSizes,
124
- [ size.slug ]: defaultUrl || mediaDetailsUrl,
125
- };
126
- },
127
- {}
128
- );
107
+ const sizes = imageSizes.reduce( ( currentSizes, size ) => {
108
+ const defaultUrl = get( image, [
109
+ 'sizes',
110
+ size.slug,
111
+ 'url',
112
+ ] );
113
+ const mediaDetailsUrl = get( image, [
114
+ 'media_details',
115
+ 'sizes',
116
+ size.slug,
117
+ 'source_url',
118
+ ] );
119
+ return {
120
+ ...currentSizes,
121
+ [ size.slug ]: defaultUrl || mediaDetailsUrl,
122
+ };
123
+ }, {} );
129
124
  return {
130
125
  ...currentResizedImages,
131
126
  [ parseInt( id, 10 ) ]: sizes,
@@ -41,6 +41,9 @@
41
41
  "blockGap": true
42
42
  }
43
43
  },
44
+ "dimensions": {
45
+ "minHeight": true
46
+ },
44
47
  "__experimentalBorder": {
45
48
  "color": true,
46
49
  "radius": true,
package/src/group/edit.js CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useSelect } from '@wordpress/data';
5
-
4
+ import { useDispatch, useSelect } from '@wordpress/data';
6
5
  import {
7
6
  InnerBlocks,
8
7
  useBlockProps,
@@ -13,30 +12,67 @@ import {
13
12
  } from '@wordpress/block-editor';
14
13
  import { SelectControl } from '@wordpress/components';
15
14
  import { __ } from '@wordpress/i18n';
15
+ import { useCallback } from '@wordpress/element';
16
16
 
17
- const htmlElementMessages = {
18
- header: __(
19
- 'The <header> element should represent introductory content, typically a group of introductory or navigational aids.'
20
- ),
21
- main: __(
22
- 'The <main> element should be used for the primary content of your document only. '
23
- ),
24
- section: __(
25
- "The <section> element should represent a standalone portion of the document that can't be better represented by another element."
26
- ),
27
- article: __(
28
- 'The <article> element should represent a self contained, syndicatable portion of the document.'
29
- ),
30
- aside: __(
31
- "The <aside> element should represent a portion of a document whose content is only indirectly related to the document's main content."
32
- ),
33
- footer: __(
34
- 'The <footer> element should represent a footer for its nearest sectioning element (e.g.: <section>, <article>, <main> etc.).'
35
- ),
36
- };
17
+ /**
18
+ * Internal dependencies
19
+ */
20
+ import GroupPlaceHolder, { useShouldShowPlaceHolder } from './placeholder';
21
+
22
+ /**
23
+ * Render inspector controls for the Group block.
24
+ *
25
+ * @param {Object} props Component props.
26
+ * @param {string} props.tagName The HTML tag name.
27
+ * @param {Function} props.onSelectTagName onChange function for the SelectControl.
28
+ *
29
+ * @return {JSX.Element} The control group.
30
+ */
31
+ function GroupEditControls( { tagName, onSelectTagName } ) {
32
+ const htmlElementMessages = {
33
+ header: __(
34
+ 'The <header> element should represent introductory content, typically a group of introductory or navigational aids.'
35
+ ),
36
+ main: __(
37
+ 'The <main> element should be used for the primary content of your document only. '
38
+ ),
39
+ section: __(
40
+ "The <section> element should represent a standalone portion of the document that can't be better represented by another element."
41
+ ),
42
+ article: __(
43
+ 'The <article> element should represent a self-contained, syndicatable portion of the document.'
44
+ ),
45
+ aside: __(
46
+ "The <aside> element should represent a portion of a document whose content is only indirectly related to the document's main content."
47
+ ),
48
+ footer: __(
49
+ 'The <footer> element should represent a footer for its nearest sectioning element (e.g.: <section>, <article>, <main> etc.).'
50
+ ),
51
+ };
52
+ return (
53
+ <InspectorControls __experimentalGroup="advanced">
54
+ <SelectControl
55
+ label={ __( 'HTML element' ) }
56
+ options={ [
57
+ { label: __( 'Default (<div>)' ), value: 'div' },
58
+ { label: '<header>', value: 'header' },
59
+ { label: '<main>', value: 'main' },
60
+ { label: '<section>', value: 'section' },
61
+ { label: '<article>', value: 'article' },
62
+ { label: '<aside>', value: 'aside' },
63
+ { label: '<footer>', value: 'footer' },
64
+ ] }
65
+ value={ tagName }
66
+ onChange={ onSelectTagName }
67
+ help={ htmlElementMessages[ tagName ] }
68
+ />
69
+ </InspectorControls>
70
+ );
71
+ }
37
72
 
38
73
  function GroupEdit( {
39
74
  attributes,
75
+ name,
40
76
  setAttributes,
41
77
  clientId,
42
78
  __unstableLayoutClassNames: layoutClassNames,
@@ -52,18 +88,26 @@ function GroupEdit( {
52
88
  },
53
89
  [ clientId ]
54
90
  );
55
- const defaultLayout = useSetting( 'layout' ) || {};
91
+
56
92
  const { tagName: TagName = 'div', templateLock, layout = {} } = attributes;
93
+
94
+ // Layout settings.
95
+ const defaultLayout = useSetting( 'layout' ) || {};
57
96
  const usedLayout = ! layout?.type
58
97
  ? { ...defaultLayout, ...layout, type: 'default' }
59
98
  : { ...defaultLayout, ...layout };
60
99
  const { type = 'default' } = usedLayout;
61
100
  const layoutSupportEnabled = themeSupportsLayout || type === 'flex';
62
101
 
102
+ // Hooks.
63
103
  const blockProps = useBlockProps( {
64
104
  className: ! layoutSupportEnabled ? layoutClassNames : null,
65
105
  } );
66
-
106
+ const [ showPlaceholder, setShowPlaceholder ] = useShouldShowPlaceHolder( {
107
+ attributes,
108
+ usedLayoutType: usedLayout?.type,
109
+ hasInnerBlocks,
110
+ } );
67
111
  const innerBlocksProps = useInnerBlocksProps(
68
112
  layoutSupportEnabled
69
113
  ? blockProps
@@ -78,31 +122,38 @@ function GroupEdit( {
78
122
  }
79
123
  );
80
124
 
125
+ const { selectBlock } = useDispatch( blockEditorStore );
126
+ const updateSelection = useCallback(
127
+ ( newClientId ) => selectBlock( newClientId, -1 ),
128
+ [ selectBlock ]
129
+ );
130
+ const selectVariation = ( nextVariation ) => {
131
+ setAttributes( nextVariation.attributes );
132
+ updateSelection( clientId );
133
+ setShowPlaceholder( false );
134
+ };
135
+
81
136
  return (
82
137
  <>
83
- <InspectorControls __experimentalGroup="advanced">
84
- <SelectControl
85
- label={ __( 'HTML element' ) }
86
- options={ [
87
- { label: __( 'Default (<div>)' ), value: 'div' },
88
- { label: '<header>', value: 'header' },
89
- { label: '<main>', value: 'main' },
90
- { label: '<section>', value: 'section' },
91
- { label: '<article>', value: 'article' },
92
- { label: '<aside>', value: 'aside' },
93
- { label: '<footer>', value: 'footer' },
94
- ] }
95
- value={ TagName }
96
- onChange={ ( value ) =>
97
- setAttributes( { tagName: value } )
98
- }
99
- help={ htmlElementMessages[ TagName ] }
138
+ <GroupEditControls
139
+ tagName={ TagName }
140
+ onSelectTagName={ ( value ) =>
141
+ setAttributes( { tagName: value } )
142
+ }
143
+ />
144
+ { showPlaceholder && (
145
+ <GroupPlaceHolder
146
+ clientId={ clientId }
147
+ name={ name }
148
+ onSelect={ selectVariation }
100
149
  />
101
- </InspectorControls>
102
- { layoutSupportEnabled && <TagName { ...innerBlocksProps } /> }
150
+ ) }
151
+ { layoutSupportEnabled && ! showPlaceholder && (
152
+ <TagName { ...innerBlocksProps } />
153
+ ) }
103
154
  { /* Ideally this is not needed but it's there for backward compatibility reason
104
155
  to keep this div for themes that might rely on its presence */ }
105
- { ! layoutSupportEnabled && (
156
+ { ! layoutSupportEnabled && ! showPlaceholder && (
106
157
  <TagName { ...blockProps }>
107
158
  <div { ...innerBlocksProps } />
108
159
  </TagName>
@@ -52,3 +52,51 @@
52
52
  pointer-events: all;
53
53
  }
54
54
  }
55
+
56
+ .wp-block-group__placeholder {
57
+ .wp-block-group-placeholder__variations {
58
+ list-style: none;
59
+ display: flex;
60
+ justify-content: center;
61
+ flex-direction: row;
62
+ flex-wrap: wrap;
63
+ width: 100%;
64
+ padding: 0;
65
+ margin: 0;
66
+ }
67
+ .components-placeholder__instructions {
68
+ text-align: center;
69
+ margin-bottom: 18px;
70
+ }
71
+ .wp-block-group-placeholder__variations svg {
72
+ fill: $gray-400 !important;
73
+ }
74
+ .wp-block-group-placeholder__variations svg:hover {
75
+ fill: var(--wp-admin-theme-color) !important;
76
+ }
77
+ .wp-block-group-placeholder__variations > li {
78
+ margin: 0 $grid-unit-15 $grid-unit-15 $grid-unit-15;
79
+ width: auto;
80
+ display: flex;
81
+ flex-direction: column;
82
+ align-items: center;
83
+ }
84
+ .wp-block-group-placeholder__variations li > .wp-block-group-placeholder__variation-button {
85
+ width: 44px;
86
+ height: 32px;
87
+ padding: 0;
88
+ &:hover {
89
+ box-shadow: none;
90
+ }
91
+ }
92
+ .components-placeholder {
93
+ min-height: auto;
94
+ padding: $grid-unit-30;
95
+ }
96
+ .is-small,
97
+ .is-medium {
98
+ .wp-block-group-placeholder__variations > li {
99
+ margin: $grid-unit-15;
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+ import { useBlockProps } from '@wordpress/block-editor';
6
+ import { __ } from '@wordpress/i18n';
7
+ import { store as blocksStore } from '@wordpress/blocks';
8
+ import { Path, SVG, Button, Placeholder } from '@wordpress/components';
9
+ import { useState, useEffect } from '@wordpress/element';
10
+
11
+ /**
12
+ * Returns a custom variation icon.
13
+ *
14
+ * @param {string} name The block variation name.
15
+ *
16
+ * @return {JSX.Element} The SVG element.
17
+ */
18
+ const getGroupPlaceholderIcons = ( name = 'group' ) => {
19
+ const icons = {
20
+ group: (
21
+ <SVG
22
+ xmlns="http://www.w3.org/2000/svg"
23
+ width="44"
24
+ height="32"
25
+ viewBox="0 0 44 32"
26
+ >
27
+ <Path
28
+ d="M42 0H2C.9 0 0 .9 0 2v28c0 1.1.9 2 2 2h40c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2z"
29
+ // style="fill:#ccc"
30
+ />
31
+ </SVG>
32
+ ),
33
+ 'group-row': (
34
+ <SVG
35
+ xmlns="http://www.w3.org/2000/svg"
36
+ width="44"
37
+ height="32"
38
+ viewBox="0 0 44 32"
39
+ >
40
+ <Path
41
+ d="M42 0H23.5c-.6 0-1 .4-1 1v30c0 .6.4 1 1 1H42c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zM20.5 0H2C.9 0 0 .9 0 2v28c0 1.1.9 2 2 2h18.5c.6 0 1-.4 1-1V1c0-.6-.4-1-1-1z"
42
+ // style="fill:#ccc"
43
+ />
44
+ </SVG>
45
+ ),
46
+ 'group-stack': (
47
+ <SVG
48
+ xmlns="http://www.w3.org/2000/svg"
49
+ width="44"
50
+ height="32"
51
+ viewBox="0 0 44 32"
52
+ >
53
+ <Path
54
+ d="M42 0H2C.9 0 0 .9 0 2v12.5c0 .6.4 1 1 1h42c.6 0 1-.4 1-1V2c0-1.1-.9-2-2-2zm1 16.5H1c-.6 0-1 .4-1 1V30c0 1.1.9 2 2 2h40c1.1 0 2-.9 2-2V17.5c0-.6-.4-1-1-1z"
55
+ // style="fill:#ccc"
56
+ />
57
+ </SVG>
58
+ ),
59
+ };
60
+ return icons?.[ name ];
61
+ };
62
+
63
+ /**
64
+ * A custom hook to tell the Group block whether to show the variation placeholder.
65
+ *
66
+ * @param {Object} props Arguments to pass to hook.
67
+ * @param {Object} [props.attributes] The block's attributes.
68
+ * @param {string} [props.usedLayoutType] The block's current layout type.
69
+ * @param {boolean} [props.hasInnerBlocks] Whether the block has inner blocks.
70
+ *
71
+ * @return {[boolean, Function]} A state value and setter function.
72
+ */
73
+ export function useShouldShowPlaceHolder( {
74
+ attributes = {
75
+ style: undefined,
76
+ backgroundColor: undefined,
77
+ textColor: undefined,
78
+ fontSize: undefined,
79
+ },
80
+ usedLayoutType = '',
81
+ hasInnerBlocks = false,
82
+ } ) {
83
+ const { style, backgroundColor, textColor, fontSize } = attributes;
84
+ /*
85
+ * Shows the placeholder when no known styles are set,
86
+ * or when a non-default layout has been selected.
87
+ * Should the Group block support more style presets in the
88
+ * future, e.g., attributes.spacingSize, we can add them to the
89
+ * condition.
90
+ */
91
+ const [ showPlaceholder, setShowPlaceholder ] = useState(
92
+ ! hasInnerBlocks &&
93
+ ! backgroundColor &&
94
+ ! fontSize &&
95
+ ! textColor &&
96
+ ! style &&
97
+ usedLayoutType !== 'flex'
98
+ );
99
+
100
+ useEffect( () => {
101
+ if (
102
+ !! hasInnerBlocks ||
103
+ !! backgroundColor ||
104
+ !! fontSize ||
105
+ !! textColor ||
106
+ !! style ||
107
+ usedLayoutType === 'flex'
108
+ ) {
109
+ setShowPlaceholder( false );
110
+ }
111
+ }, [
112
+ backgroundColor,
113
+ fontSize,
114
+ textColor,
115
+ style,
116
+ usedLayoutType,
117
+ hasInnerBlocks,
118
+ ] );
119
+
120
+ return [ showPlaceholder, setShowPlaceholder ];
121
+ }
122
+
123
+ /**
124
+ * Display group variations if none is selected.
125
+ *
126
+ * @param {Object} props Component props.
127
+ * @param {string} props.name The block's name.
128
+ * @param {Function} props.onSelect Function to set block's attributes.
129
+ *
130
+ * @return {JSX.Element} The placeholder.
131
+ */
132
+ function GroupPlaceHolder( { name, onSelect } ) {
133
+ const { defaultVariation, variations } = useSelect(
134
+ ( select ) => {
135
+ const { getBlockVariations, getDefaultBlockVariation } =
136
+ select( blocksStore );
137
+ return {
138
+ defaultVariation: getDefaultBlockVariation( name, 'block' ),
139
+ variations: getBlockVariations( name, 'block' ) || [],
140
+ };
141
+ },
142
+ [ name ]
143
+ );
144
+ const blockProps = useBlockProps( {
145
+ className: 'wp-block-group__placeholder',
146
+ } );
147
+ const selectVariation = ( nextVariation = defaultVariation ) =>
148
+ onSelect( nextVariation );
149
+
150
+ return (
151
+ <div { ...blockProps }>
152
+ <Placeholder
153
+ instructions={ __( 'Group blocks together. Select a layout:' ) }
154
+ >
155
+ { /*
156
+ * Taken from BlockVariationPicker component.
157
+ * Disable reason: The `list` ARIA role is redundant but
158
+ * Safari+VoiceOver won't announce the list otherwise.
159
+ */
160
+ /* eslint-disable jsx-a11y/no-redundant-roles */ }
161
+ <ul
162
+ role="list"
163
+ className="wp-block-group-placeholder__variations"
164
+ aria-label={ __( 'Block variations' ) }
165
+ >
166
+ { variations.map( ( variation ) => (
167
+ <li key={ variation.name }>
168
+ <Button
169
+ variant="tertiary"
170
+ icon={ getGroupPlaceholderIcons(
171
+ variation.name
172
+ ) }
173
+ iconSize={ 44 }
174
+ onClick={ () => selectVariation( variation ) }
175
+ className="wp-block-group-placeholder__variation-button"
176
+ label={ `${ variation.title }: ${ variation.description }` }
177
+ />
178
+ </li>
179
+ ) ) }
180
+ </ul>
181
+ { /* eslint-enable jsx-a11y/no-redundant-roles */ }
182
+ </Placeholder>
183
+ </div>
184
+ );
185
+ }
186
+
187
+ export default GroupPlaceHolder;
@@ -79,14 +79,14 @@ describe( 'Group block', () => {
79
79
  const screen = await initializeEditor( {
80
80
  initialHtml: NESTED_GROUP_BLOCK,
81
81
  } );
82
- const { getByA11yLabel } = screen;
82
+ const { getByLabelText } = screen;
83
83
 
84
84
  // Get block
85
85
  let groupBlock = await getBlock( screen, 'Group' );
86
86
  fireEvent.press( groupBlock );
87
87
 
88
88
  // Get Ungroup button
89
- let ungroupButton = getByA11yLabel( /Ungroup/ );
89
+ let ungroupButton = getByLabelText( /Ungroup/ );
90
90
  fireEvent.press( ungroupButton );
91
91
 
92
92
  // Press Group block again
@@ -94,7 +94,7 @@ describe( 'Group block', () => {
94
94
  fireEvent.press( groupBlock );
95
95
 
96
96
  // Ungroup last block
97
- ungroupButton = getByA11yLabel( /Ungroup/ );
97
+ ungroupButton = getByLabelText( /Ungroup/ );
98
98
  fireEvent.press( ungroupButton );
99
99
 
100
100
  expect( getEditorHtml() ).toMatchSnapshot();
@@ -0,0 +1,78 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render } from '@testing-library/react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { useShouldShowPlaceHolder } from '../placeholder';
10
+
11
+ describe( 'useShouldShowPlaceHolder', () => {
12
+ function renderTestComponent( ...props ) {
13
+ let returnVal = [];
14
+ function TestComponent() {
15
+ returnVal = returnVal.concat(
16
+ useShouldShowPlaceHolder( ...props )
17
+ );
18
+ return null;
19
+ }
20
+ render( <TestComponent /> );
21
+ return returnVal;
22
+ }
23
+
24
+ test.each( [
25
+ {
26
+ attributes: {},
27
+ usedLayoutType: undefined,
28
+ hasInnerBlocks: undefined,
29
+ expectedValue: true,
30
+ },
31
+ {
32
+ attributes: { style: { something: 'something' } },
33
+ usedLayoutType: undefined,
34
+ hasInnerBlocks: undefined,
35
+ expectedValue: false,
36
+ },
37
+ {
38
+ attributes: { backgroundColor: 'red' },
39
+ usedLayoutType: undefined,
40
+ hasInnerBlocks: true,
41
+ expectedValue: false,
42
+ },
43
+ {
44
+ attributes: { fontSize: 'big' },
45
+ usedLayoutType: undefined,
46
+ hasInnerBlocks: true,
47
+ expectedValue: false,
48
+ },
49
+ {
50
+ attributes: { textColor: 'yellow' },
51
+ usedLayoutType: undefined,
52
+ hasInnerBlocks: true,
53
+ expectedValue: false,
54
+ },
55
+ {
56
+ attributes: undefined,
57
+ usedLayoutType: 'flex',
58
+ hasInnerBlocks: undefined,
59
+ expectedValue: false,
60
+ },
61
+ {
62
+ attributes: undefined,
63
+ usedLayoutType: undefined,
64
+ hasInnerBlocks: true,
65
+ expectedValue: false,
66
+ },
67
+ ] )(
68
+ 'should return $expectedValue for `showPlaceholder` when hasInnerBlocks is $hasInnerBlocks, usedLayoutType is $usedLayoutType and attributes is $attributes',
69
+ ( { hasInnerBlocks, usedLayoutType, attributes, expectedValue } ) => {
70
+ const [ showPlaceholder ] = renderTestComponent( {
71
+ hasInnerBlocks,
72
+ usedLayoutType,
73
+ attributes,
74
+ } );
75
+ expect( showPlaceholder ).toBe( expectedValue );
76
+ }
77
+ );
78
+ } );
@@ -11,7 +11,7 @@ const variations = [
11
11
  description: __( 'Gather blocks in a container.' ),
12
12
  attributes: { layout: { type: 'constrained' } },
13
13
  isDefault: true,
14
- scope: [ 'inserter', 'transform' ],
14
+ scope: [ 'block', 'inserter', 'transform' ],
15
15
  isActive: ( blockAttributes ) =>
16
16
  ! blockAttributes.layout ||
17
17
  ! blockAttributes.layout?.type ||
@@ -24,7 +24,7 @@ const variations = [
24
24
  title: _x( 'Row', 'single horizontal line' ),
25
25
  description: __( 'Arrange blocks horizontally.' ),
26
26
  attributes: { layout: { type: 'flex', flexWrap: 'nowrap' } },
27
- scope: [ 'inserter', 'transform' ],
27
+ scope: [ 'block', 'inserter', 'transform' ],
28
28
  isActive: ( blockAttributes ) =>
29
29
  blockAttributes.layout?.type === 'flex' &&
30
30
  ( ! blockAttributes.layout?.orientation ||
@@ -36,7 +36,7 @@ const variations = [
36
36
  title: __( 'Stack' ),
37
37
  description: __( 'Arrange blocks vertically.' ),
38
38
  attributes: { layout: { type: 'flex', orientation: 'vertical' } },
39
- scope: [ 'inserter', 'transform' ],
39
+ scope: [ 'block', 'inserter', 'transform' ],
40
40
  isActive: ( blockAttributes ) =>
41
41
  blockAttributes.layout?.type === 'flex' &&
42
42
  blockAttributes.layout?.orientation === 'vertical',