@wordpress/block-editor 15.21.1 → 15.22.1-next.v.202606191442.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 (186) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/block-card/index.cjs +4 -5
  3. package/build/components/block-card/index.cjs.map +2 -2
  4. package/build/components/block-inspector/index.cjs +3 -4
  5. package/build/components/block-inspector/index.cjs.map +2 -2
  6. package/build/components/block-mover/mover-description.cjs +6 -6
  7. package/build/components/block-mover/mover-description.cjs.map +1 -1
  8. package/build/components/block-patterns-list/index.cjs.map +2 -2
  9. package/build/components/block-patterns-paging/index.cjs +4 -4
  10. package/build/components/block-patterns-paging/index.cjs.map +2 -2
  11. package/build/components/block-popover/use-popover-scroll.cjs.map +1 -1
  12. package/build/components/block-quick-navigation/index.cjs +7 -5
  13. package/build/components/block-quick-navigation/index.cjs.map +2 -2
  14. package/build/components/block-toolbar/edit-section-button.cjs.map +1 -1
  15. package/build/components/block-tools/use-block-toolbar-popover-props.cjs +10 -4
  16. package/build/components/block-tools/use-block-toolbar-popover-props.cjs.map +2 -2
  17. package/build/components/child-layout-control/index.cjs +51 -19
  18. package/build/components/child-layout-control/index.cjs.map +2 -2
  19. package/build/components/dimensions-tool/index.cjs +4 -5
  20. package/build/components/dimensions-tool/index.cjs.map +2 -2
  21. package/build/components/dimensions-tool/scale-tool.cjs +1 -1
  22. package/build/components/dimensions-tool/scale-tool.cjs.map +2 -2
  23. package/build/components/global-styles/dimensions-panel.cjs +1 -1
  24. package/build/components/global-styles/dimensions-panel.cjs.map +2 -2
  25. package/build/components/global-styles/shadow-panel-components.cjs.map +2 -2
  26. package/build/components/grid/grid-item-resizer.cjs.map +1 -1
  27. package/build/components/grid/grid-visualizer.cjs.map +1 -1
  28. package/build/components/iframe/index.cjs +6 -3
  29. package/build/components/iframe/index.cjs.map +3 -3
  30. package/build/components/image-editor/use-transform-image.cjs.map +1 -1
  31. package/build/components/inserter/get-appender-label.cjs.map +1 -1
  32. package/build/components/inserter/media-tab/media-preview.cjs.map +2 -2
  33. package/build/components/inserter/search-items.cjs.map +1 -1
  34. package/build/components/inspector-controls/fill.cjs +1 -3
  35. package/build/components/inspector-controls/fill.cjs.map +2 -2
  36. package/build/components/inspector-controls-tabs/index.cjs.map +2 -2
  37. package/build/components/inspector-controls-tabs/styles-tab.cjs +1 -1
  38. package/build/components/inspector-controls-tabs/styles-tab.cjs.map +2 -2
  39. package/build/components/keyboard-shortcuts/index.cjs.map +1 -1
  40. package/build/components/line-height-control/index.cjs.map +1 -1
  41. package/build/components/link-control/index.cjs +1 -1
  42. package/build/components/link-control/index.cjs.map +1 -1
  43. package/build/components/link-control/normalize-url.cjs.map +1 -1
  44. package/build/components/list-view/block-select-button.cjs.map +2 -2
  45. package/build/components/list-view/utils.cjs.map +1 -1
  46. package/build/components/preset-input-control/custom-value-controls.cjs.map +2 -2
  47. package/build/components/provider/index.cjs.map +1 -1
  48. package/build/components/provider/use-block-sync.cjs.map +1 -1
  49. package/build/components/rich-text/event-listeners/before-input-rules.cjs +1 -1
  50. package/build/components/rich-text/event-listeners/before-input-rules.cjs.map +2 -2
  51. package/build/components/rich-text/event-listeners/paste-handler.cjs.map +1 -1
  52. package/build/components/rich-text/prevent-event-discovery.cjs +1 -1
  53. package/build/components/rich-text/prevent-event-discovery.cjs.map +1 -1
  54. package/build/components/writing-flow/use-tab-nav.cjs.map +1 -1
  55. package/build/hooks/anchor.cjs +1 -1
  56. package/build/hooks/anchor.cjs.map +1 -1
  57. package/build/hooks/background.cjs.map +1 -1
  58. package/build/hooks/block-fields/index.cjs +13 -2
  59. package/build/hooks/block-fields/index.cjs.map +2 -2
  60. package/build/hooks/dimensions.cjs +13 -2
  61. package/build/hooks/dimensions.cjs.map +2 -2
  62. package/build/hooks/layout-child.cjs +27 -2
  63. package/build/hooks/layout-child.cjs.map +2 -2
  64. package/build/hooks/list-view.cjs +10 -7
  65. package/build/hooks/list-view.cjs.map +2 -2
  66. package/build/hooks/spacing-visualizer.cjs.map +1 -1
  67. package/build/hooks/state-utils.cjs.map +1 -1
  68. package/build/hooks/style.cjs +42 -2
  69. package/build/hooks/style.cjs.map +2 -2
  70. package/build/private-apis.cjs +4 -0
  71. package/build/private-apis.cjs.map +2 -2
  72. package/build/store/actions.cjs.map +1 -1
  73. package/build/store/private-selectors.cjs +19 -2
  74. package/build/store/private-selectors.cjs.map +2 -2
  75. package/build/store/reducer.cjs.map +1 -1
  76. package/build/store/selectors.cjs.map +1 -1
  77. package/build/utils/dom.cjs.map +1 -1
  78. package/build/utils/selection.cjs +1 -1
  79. package/build-module/components/block-card/index.mjs +5 -6
  80. package/build-module/components/block-card/index.mjs.map +2 -2
  81. package/build-module/components/block-inspector/index.mjs +3 -5
  82. package/build-module/components/block-inspector/index.mjs.map +2 -2
  83. package/build-module/components/block-mover/mover-description.mjs +6 -6
  84. package/build-module/components/block-mover/mover-description.mjs.map +1 -1
  85. package/build-module/components/block-patterns-list/index.mjs.map +2 -2
  86. package/build-module/components/block-patterns-paging/index.mjs +4 -4
  87. package/build-module/components/block-patterns-paging/index.mjs.map +2 -2
  88. package/build-module/components/block-popover/use-popover-scroll.mjs.map +1 -1
  89. package/build-module/components/block-quick-navigation/index.mjs +8 -6
  90. package/build-module/components/block-quick-navigation/index.mjs.map +2 -2
  91. package/build-module/components/block-toolbar/edit-section-button.mjs.map +1 -1
  92. package/build-module/components/block-tools/use-block-toolbar-popover-props.mjs +10 -4
  93. package/build-module/components/block-tools/use-block-toolbar-popover-props.mjs.map +2 -2
  94. package/build-module/components/child-layout-control/index.mjs +51 -19
  95. package/build-module/components/child-layout-control/index.mjs.map +2 -2
  96. package/build-module/components/dimensions-tool/index.mjs +4 -5
  97. package/build-module/components/dimensions-tool/index.mjs.map +2 -2
  98. package/build-module/components/dimensions-tool/scale-tool.mjs +1 -1
  99. package/build-module/components/dimensions-tool/scale-tool.mjs.map +2 -2
  100. package/build-module/components/global-styles/dimensions-panel.mjs +1 -2
  101. package/build-module/components/global-styles/dimensions-panel.mjs.map +2 -2
  102. package/build-module/components/global-styles/shadow-panel-components.mjs.map +2 -2
  103. package/build-module/components/grid/grid-item-resizer.mjs.map +1 -1
  104. package/build-module/components/grid/grid-visualizer.mjs.map +1 -1
  105. package/build-module/components/iframe/index.mjs +6 -3
  106. package/build-module/components/iframe/index.mjs.map +2 -2
  107. package/build-module/components/image-editor/use-transform-image.mjs.map +1 -1
  108. package/build-module/components/inserter/get-appender-label.mjs.map +1 -1
  109. package/build-module/components/inserter/media-tab/media-preview.mjs.map +2 -2
  110. package/build-module/components/inserter/search-items.mjs.map +1 -1
  111. package/build-module/components/inspector-controls/fill.mjs +1 -3
  112. package/build-module/components/inspector-controls/fill.mjs.map +2 -2
  113. package/build-module/components/inspector-controls-tabs/index.mjs.map +2 -2
  114. package/build-module/components/inspector-controls-tabs/styles-tab.mjs +1 -1
  115. package/build-module/components/inspector-controls-tabs/styles-tab.mjs.map +2 -2
  116. package/build-module/components/keyboard-shortcuts/index.mjs.map +1 -1
  117. package/build-module/components/line-height-control/index.mjs.map +1 -1
  118. package/build-module/components/link-control/index.mjs +1 -1
  119. package/build-module/components/link-control/index.mjs.map +1 -1
  120. package/build-module/components/link-control/normalize-url.mjs.map +1 -1
  121. package/build-module/components/list-view/block-select-button.mjs.map +2 -2
  122. package/build-module/components/list-view/utils.mjs.map +1 -1
  123. package/build-module/components/preset-input-control/custom-value-controls.mjs.map +2 -2
  124. package/build-module/components/provider/index.mjs.map +1 -1
  125. package/build-module/components/provider/use-block-sync.mjs.map +1 -1
  126. package/build-module/components/rich-text/event-listeners/before-input-rules.mjs +1 -1
  127. package/build-module/components/rich-text/event-listeners/before-input-rules.mjs.map +2 -2
  128. package/build-module/components/rich-text/event-listeners/paste-handler.mjs.map +1 -1
  129. package/build-module/components/rich-text/prevent-event-discovery.mjs +1 -1
  130. package/build-module/components/rich-text/prevent-event-discovery.mjs.map +1 -1
  131. package/build-module/components/writing-flow/use-tab-nav.mjs.map +1 -1
  132. package/build-module/hooks/anchor.mjs +1 -1
  133. package/build-module/hooks/anchor.mjs.map +1 -1
  134. package/build-module/hooks/background.mjs.map +1 -1
  135. package/build-module/hooks/block-fields/index.mjs +13 -2
  136. package/build-module/hooks/block-fields/index.mjs.map +2 -2
  137. package/build-module/hooks/dimensions.mjs +12 -2
  138. package/build-module/hooks/dimensions.mjs.map +2 -2
  139. package/build-module/hooks/layout-child.mjs +27 -2
  140. package/build-module/hooks/layout-child.mjs.map +2 -2
  141. package/build-module/hooks/list-view.mjs +10 -7
  142. package/build-module/hooks/list-view.mjs.map +2 -2
  143. package/build-module/hooks/spacing-visualizer.mjs.map +1 -1
  144. package/build-module/hooks/state-utils.mjs.map +1 -1
  145. package/build-module/hooks/style.mjs +44 -3
  146. package/build-module/hooks/style.mjs.map +2 -2
  147. package/build-module/private-apis.mjs +8 -0
  148. package/build-module/private-apis.mjs.map +2 -2
  149. package/build-module/store/actions.mjs.map +1 -1
  150. package/build-module/store/private-selectors.mjs +18 -1
  151. package/build-module/store/private-selectors.mjs.map +2 -2
  152. package/build-module/store/reducer.mjs.map +1 -1
  153. package/build-module/store/selectors.mjs.map +1 -1
  154. package/build-module/utils/dom.mjs.map +1 -1
  155. package/build-module/utils/selection.mjs +1 -1
  156. package/package.json +45 -40
  157. package/src/components/block-card/index.js +7 -13
  158. package/src/components/block-inspector/index.js +2 -10
  159. package/src/components/block-patterns-list/index.js +0 -1
  160. package/src/components/block-quick-navigation/index.js +7 -7
  161. package/src/components/block-tools/use-block-toolbar-popover-props.js +10 -4
  162. package/src/components/child-layout-control/index.js +65 -20
  163. package/src/components/child-layout-control/test/index.js +64 -0
  164. package/src/components/dimensions-tool/index.js +4 -9
  165. package/src/components/dimensions-tool/scale-tool.js +1 -2
  166. package/src/components/dimensions-tool/test/index.js +89 -11
  167. package/src/components/global-styles/dimensions-panel.js +1 -2
  168. package/src/components/global-styles/shadow-panel-components.js +0 -1
  169. package/src/components/iframe/index.js +8 -3
  170. package/src/components/inserter/media-tab/media-preview.js +0 -1
  171. package/src/components/inspector-controls/fill.js +6 -8
  172. package/src/components/inspector-controls-tabs/index.js +0 -1
  173. package/src/components/inspector-controls-tabs/styles-tab.js +5 -1
  174. package/src/components/list-view/block-select-button.js +0 -1
  175. package/src/components/preset-input-control/custom-value-controls.js +0 -1
  176. package/src/hooks/block-fields/index.js +27 -2
  177. package/src/hooks/dimensions.js +14 -3
  178. package/src/hooks/layout-child.js +41 -2
  179. package/src/hooks/list-view.js +10 -11
  180. package/src/hooks/style.js +80 -2
  181. package/src/hooks/test/dimensions.js +71 -0
  182. package/src/hooks/test/layout-child.js +165 -0
  183. package/src/hooks/test/style.js +126 -4
  184. package/src/private-apis.js +8 -0
  185. package/src/store/private-selectors.js +47 -0
  186. package/src/store/test/private-selectors.js +138 -0
@@ -16,6 +16,17 @@ import { useState } from '@wordpress/element';
16
16
  import DimensionsTool from '../';
17
17
 
18
18
  const EMPTY_OBJECT = {};
19
+ const ASPECT_RATIO_OPTIONS = [
20
+ { label: 'Original', value: 'auto' },
21
+ { label: '16/9', value: '16/9' },
22
+ { label: '4/3', value: '4/3' },
23
+ {
24
+ label: 'Custom',
25
+ value: 'custom',
26
+ disabled: true,
27
+ hidden: true,
28
+ },
29
+ ];
19
30
 
20
31
  function Example( { initialValue, onChange, ...props } ) {
21
32
  const [ value, setValue ] = useState( initialValue );
@@ -33,16 +44,27 @@ function Example( { initialValue, onChange, ...props } ) {
33
44
  } }
34
45
  defaultScale="cover"
35
46
  defaultAspectRatio="auto"
36
- aspectRatioOptions={ [
37
- { label: 'Original', value: 'auto' },
38
- { label: '16/9', value: '16/9' },
39
- {
40
- label: 'Custom',
41
- value: 'custom',
42
- disabled: true,
43
- hidden: true,
44
- },
45
- ] }
47
+ aspectRatioOptions={ ASPECT_RATIO_OPTIONS }
48
+ value={ value }
49
+ { ...props }
50
+ />
51
+ </ToolsPanel>
52
+ );
53
+ }
54
+
55
+ function ControlledExample( { value, onChange, ...props } ) {
56
+ return (
57
+ <ToolsPanel
58
+ label="Dimensions"
59
+ panelId="panel-id"
60
+ resetAll={ () => onChange( EMPTY_OBJECT ) }
61
+ >
62
+ <DimensionsTool
63
+ panelId="panel-id"
64
+ onChange={ onChange }
65
+ defaultScale="cover"
66
+ defaultAspectRatio="auto"
67
+ aspectRatioOptions={ ASPECT_RATIO_OPTIONS }
46
68
  value={ value }
47
69
  { ...props }
48
70
  />
@@ -60,6 +82,39 @@ function Example( { initialValue, onChange, ...props } ) {
60
82
  // properties are treated differently from missing properties.
61
83
 
62
84
  describe( 'DimensionsTool', () => {
85
+ describe( 'controlled values', () => {
86
+ it( 'updates the aspect ratio control when the value prop changes', () => {
87
+ const onChange = jest.fn();
88
+ const { rerender } = render(
89
+ <ControlledExample
90
+ value={ { aspectRatio: '16/9' } }
91
+ onChange={ onChange }
92
+ />
93
+ );
94
+ const aspectRatioSelect = screen.getByRole( 'combobox', {
95
+ name: 'Aspect ratio',
96
+ } );
97
+
98
+ expect( aspectRatioSelect ).toHaveValue( '16/9' );
99
+
100
+ rerender(
101
+ <ControlledExample
102
+ value={ { aspectRatio: '4/3' } }
103
+ onChange={ onChange }
104
+ />
105
+ );
106
+ expect( aspectRatioSelect ).toHaveValue( '4/3' );
107
+
108
+ rerender(
109
+ <ControlledExample
110
+ value={ EMPTY_OBJECT }
111
+ onChange={ onChange }
112
+ />
113
+ );
114
+ expect( aspectRatioSelect ).toHaveValue( 'auto' );
115
+ } );
116
+ } );
117
+
63
118
  describe( 'updating aspectRatio', () => {
64
119
  it( 'when starting with empty initial state, setting aspectRatio also sets scale (0000) -> (1100)', async () => {
65
120
  const user = userEvent.setup();
@@ -323,7 +378,30 @@ describe( 'DimensionsTool', () => {
323
378
  } );
324
379
 
325
380
  describe( 'updating scale', () => {
326
- // No custom interactions here. Things should just update normally.
381
+ it( 'when default scale is cover, setting scale to fill preserves the fill value', async () => {
382
+ const user = userEvent.setup();
383
+ const onChange = jest.fn();
384
+
385
+ const initialValue = {
386
+ aspectRatio: '16/9',
387
+ scale: 'cover',
388
+ };
389
+
390
+ render(
391
+ <Example initialValue={ initialValue } onChange={ onChange } />
392
+ );
393
+
394
+ const scaleFillRadio = screen.getByRole( 'radio', {
395
+ name: 'Fill',
396
+ } );
397
+
398
+ await user.click( scaleFillRadio );
399
+ expect( scaleFillRadio ).toBeChecked();
400
+
401
+ expect( onChange.mock.calls ).toStrictEqual( [
402
+ [ { aspectRatio: '16/9', scale: 'fill' } ],
403
+ ] );
404
+ } );
327
405
  } );
328
406
 
329
407
  describe( 'updating dimensions', () => {
@@ -32,7 +32,6 @@ import { setImmutably } from '../../utils/object';
32
32
  import {
33
33
  DEFAULT_BLOCK_STYLE_STATE,
34
34
  hasPseudoBlockStyleState,
35
- isDefaultBlockStyleState,
36
35
  hasViewportBlockStyleState,
37
36
  } from '../../hooks/block-style-state';
38
37
 
@@ -95,7 +94,7 @@ function hasWidth( settings ) {
95
94
 
96
95
  function hasAspectRatio( settings, styleState = DEFAULT_BLOCK_STYLE_STATE ) {
97
96
  return (
98
- isDefaultBlockStyleState( styleState ) &&
97
+ ! hasPseudoBlockStyleState( styleState ) &&
99
98
  settings?.dimensions?.aspectRatio
100
99
  );
101
100
  }
@@ -20,7 +20,6 @@ import { shadow as shadowIcon, Icon, check, reset } from '@wordpress/icons';
20
20
  */
21
21
  import clsx from 'clsx';
22
22
 
23
- // eslint-disable-next-line @wordpress/use-recommended-components -- `Tooltip` is not yet on the recommended `@wordpress/ui` allow-list; landing as a migration step ahead of the wider rollout.
24
23
  import { Tooltip } from '@wordpress/ui';
25
24
 
26
25
  /**
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import clsx from 'clsx';
5
+ import { version as reactVersion } from 'react';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -102,6 +103,12 @@ function getIframeSrc( resolvedAssets ) {
102
103
  return src;
103
104
  }
104
105
 
106
+ let body = '';
107
+ if ( reactVersion.split( '.' )[ 0 ] === '18' ) {
108
+ body =
109
+ '<body><script>document.currentScript.parentElement.remove()</script></body>';
110
+ }
111
+
105
112
  // Correct doctype is required to enable rendering in standards mode.
106
113
  // Also preload the styles to avoid a flash of unstyled content.
107
114
  const html = `<!doctype html>
@@ -125,9 +132,7 @@ function getIframeSrc( resolvedAssets ) {
125
132
  ${ resolvedAssets.styles ?? '' }
126
133
  ${ resolvedAssets.scripts ?? '' }
127
134
  </head>
128
- <body>
129
- <script>document.currentScript.parentElement.remove()</script>
130
- </body>
135
+ ${ body }
131
136
  </html>`;
132
137
 
133
138
  src = URL.createObjectURL( new Blob( [ html ], { type: 'text/html' } ) );
@@ -27,7 +27,6 @@ import { store as noticesStore } from '@wordpress/notices';
27
27
  import { isBlobURL } from '@wordpress/blob';
28
28
  import { getFilename } from '@wordpress/url';
29
29
 
30
- // eslint-disable-next-line @wordpress/use-recommended-components -- `Tooltip` is not yet on the recommended `@wordpress/ui` allow-list; landing as a migration step ahead of the wider rollout.
31
30
  import { Tooltip } from '@wordpress/ui';
32
31
 
33
32
  /**
@@ -26,7 +26,6 @@ import {
26
26
  import { ListViewContentFill } from './list-view-content-popover';
27
27
 
28
28
  const PATTERN_EDITING_GROUPS = [ 'content', 'list' ];
29
- const TEMPLATE_PART_GROUPS = [ 'default', 'settings', 'advanced' ];
30
29
 
31
30
  export default function InspectorControlsFill( {
32
31
  children,
@@ -56,17 +55,16 @@ export default function InspectorControlsFill( {
56
55
 
57
56
  // During pattern editing:
58
57
  // - All blocks can show pattern editing groups (content, list).
59
- // - Template parts can show a settings tab (default, settings, advanced groups).
58
+ // - Template parts can show any inspector group.
60
59
  // - Other blocks cannot show a settings tab.
61
60
  if ( context[ mayDisplayPatternEditingControlsKey ] ) {
62
- // Template parts are allowed to show a settings tab to allow access to the
63
- // 'Design' and 'Advanced' panels.
61
+ // Template parts have also historically supported
62
+ // any block inspector groups for extenders. The settings
63
+ // tab is also used by core for the 'Design' panel. Specifically
64
+ // for that block the restrictions on allowed groups are lessened.
64
65
  const isTemplatePart = context.name === 'core/template-part';
65
- const isTemplatePartGroup = TEMPLATE_PART_GROUPS.includes( group );
66
66
  const isPatternEditingGroup = PATTERN_EDITING_GROUPS.includes( group );
67
-
68
- const canShowGroup =
69
- ( isTemplatePart && isTemplatePartGroup ) || isPatternEditingGroup;
67
+ const canShowGroup = isTemplatePart || isPatternEditingGroup;
70
68
 
71
69
  if ( ! canShowGroup ) {
72
70
  return null;
@@ -9,7 +9,6 @@ import { useEffect, useState, useRef } from '@wordpress/element';
9
9
  import { store as preferencesStore } from '@wordpress/preferences';
10
10
  import { useSelect, useDispatch } from '@wordpress/data';
11
11
 
12
- // eslint-disable-next-line @wordpress/use-recommended-components -- `Tooltip` is not yet on the recommended `@wordpress/ui` allow-list; landing as a migration step ahead of the wider rollout.
13
12
  import { Tooltip } from '@wordpress/ui';
14
13
 
15
14
  /**
@@ -79,7 +79,11 @@ const StylesTab = ( {
79
79
  contentClientIds={ contentClientIds }
80
80
  />
81
81
  ) }
82
- { ! isSectionBlock && (
82
+ {
83
+ // Extenders have in the past always been allowed to add controls to group
84
+ // the restrictions are lessened for that block.
85
+ }
86
+ { ( ! isSectionBlock || blockName === 'core/template-part' ) && (
83
87
  <>
84
88
  <InspectorControls.Slot
85
89
  group="color"
@@ -22,7 +22,6 @@ import {
22
22
  import { SPACE, ENTER } from '@wordpress/keycodes';
23
23
  import { useSelect } from '@wordpress/data';
24
24
 
25
- // eslint-disable-next-line @wordpress/use-recommended-components -- `Tooltip` is not yet on the recommended `@wordpress/ui` allow-list; landing as a migration step ahead of the wider rollout.
26
25
  import { Tooltip } from '@wordpress/ui';
27
26
 
28
27
  /**
@@ -6,7 +6,6 @@ import {
6
6
  __experimentalUnitControl as UnitControl,
7
7
  } from '@wordpress/components';
8
8
 
9
- // eslint-disable-next-line @wordpress/use-recommended-components -- `Tooltip` is not yet on the recommended `@wordpress/ui` allow-list; landing as a migration step ahead of the wider rollout.
10
9
  import { Tooltip } from '@wordpress/ui';
11
10
 
12
11
  /**
@@ -14,6 +14,7 @@ import { useContext, useState, useMemo } from '@wordpress/element';
14
14
  import { __ } from '@wordpress/i18n';
15
15
  import { store as blockEditorStore } from '../../store';
16
16
  import { unlock } from '../../lock-unlock';
17
+ import { replacePatternOverridesDefaultBinding } from '../../utils/block-bindings';
17
18
  import BlockContext from '../../components/block-context';
18
19
  import BlockIcon from '../../components/block-icon';
19
20
  import useBlockDisplayTitle from '../../components/block-title/use-block-display-title';
@@ -80,15 +81,39 @@ function BlockFields( {
80
81
  return _attributes;
81
82
  }
82
83
 
84
+ /*
85
+ * The pattern overrides `__default` binding is a placeholder, not
86
+ * a real attribute: expand it to the block's bindable attributes
87
+ * before resolving values, as other bindings consumers do.
88
+ */
89
+ const { __experimentalBlockBindingsSupportedAttributes } =
90
+ select( blockEditorStore ).getSettings();
91
+ const bindableAttributes =
92
+ __experimentalBlockBindingsSupportedAttributes?.[
93
+ blockType?.name
94
+ ];
95
+ const bindings = bindableAttributes
96
+ ? replacePatternOverridesDefaultBinding(
97
+ _attributes.metadata.bindings,
98
+ bindableAttributes
99
+ )
100
+ : _attributes.metadata.bindings;
101
+
83
102
  const { getBlockBindingsSource } = unlock( select( blocksStore ) );
84
- return Object.entries( _attributes.metadata.bindings ).reduce(
103
+ return Object.entries( bindings ).reduce(
85
104
  ( acc, [ attribute, binding ] ) => {
105
+ // Skip a `__default` binding that could not be expanded:
106
+ // it is not a real block attribute.
107
+ if ( attribute === '__default' ) {
108
+ return acc;
109
+ }
86
110
  const source = getBlockBindingsSource( binding.source );
87
111
  if ( ! source ) {
88
112
  return acc;
89
113
  }
90
114
  const values = source.getValues( {
91
115
  select,
116
+ clientId,
92
117
  context: blockContext,
93
118
  bindings: { [ attribute ]: binding },
94
119
  } );
@@ -97,7 +122,7 @@ function BlockFields( {
97
122
  _attributes
98
123
  );
99
124
  },
100
- [ blockContext, clientId ]
125
+ [ blockContext, clientId, blockType?.name ]
101
126
  );
102
127
  const { selectBlock, toggleBlockHighlight } =
103
128
  useDispatch( blockEditorStore );
@@ -190,6 +190,14 @@ export function hasDimensionsSupport( blockName, feature = 'any' ) {
190
190
  return !! support?.[ feature ];
191
191
  }
192
192
 
193
+ export function isExplicitAspectRatio( aspectRatio ) {
194
+ if ( ! aspectRatio ) {
195
+ return false;
196
+ }
197
+
198
+ return `${ aspectRatio }`.trim().toLowerCase() !== 'auto';
199
+ }
200
+
193
201
  export default {
194
202
  useBlockProps,
195
203
  attributeKeys: [ 'height', 'minHeight', 'width', 'style' ],
@@ -206,8 +214,11 @@ function useBlockProps( { name, height, minHeight, style } ) {
206
214
  return {};
207
215
  }
208
216
 
217
+ const hasExplicitAspectRatio = isExplicitAspectRatio(
218
+ style?.dimensions?.aspectRatio
219
+ );
209
220
  const className = clsx( {
210
- 'has-aspect-ratio': !! style?.dimensions?.aspectRatio,
221
+ 'has-aspect-ratio': hasExplicitAspectRatio,
211
222
  } );
212
223
 
213
224
  // Allow dimensions-based inline style overrides to override any global styles rules that
@@ -215,12 +226,12 @@ function useBlockProps( { name, height, minHeight, style } ) {
215
226
  const inlineStyleOverrides = {};
216
227
 
217
228
  // Apply rules to unset incompatible styles.
218
- // Note that a set `aspectRatio` will win out if both an aspect ratio and height-related properties are set.
229
+ // Note that an explicit `aspectRatio` will win out if both an aspect ratio and height-related properties are set.
219
230
  // This is because the aspect ratio is a newer block support, so (in theory) any aspect ratio
220
231
  // that is set should be intentional and should override any existing height properties. The Cover block
221
232
  // and dimensions controls have logic that will manually clear the aspect ratio if height properties
222
233
  // are set.
223
- if ( style?.dimensions?.aspectRatio ) {
234
+ if ( hasExplicitAspectRatio ) {
224
235
  // To ensure the aspect ratio does not get overridden by `minHeight` or `height` unset any existing rule.
225
236
  inlineStyleOverrides.minHeight = 'unset';
226
237
  inlineStyleOverrides.height = 'unset';
@@ -28,6 +28,24 @@ const RESPONSIVE_BREAKPOINTS = {
28
28
  tablet: '@media (480px < width <= 782px)',
29
29
  };
30
30
 
31
+ // These are the serialized `selfStretch` values. `max` used to be called
32
+ // "Fixed" in the UI, but was renamed and replaced by `fixedNoShrink`.
33
+ const FLEX_CHILD_LAYOUT_VALUES = {
34
+ fit: 'fit',
35
+ grow: 'fill',
36
+ max: 'fixed',
37
+ fixed: 'fixedNoShrink',
38
+ };
39
+
40
+ const FLEX_SIZE_VALUES = [
41
+ FLEX_CHILD_LAYOUT_VALUES.max,
42
+ FLEX_CHILD_LAYOUT_VALUES.fixed,
43
+ ];
44
+
45
+ function isFlexSizeValue( value ) {
46
+ return FLEX_SIZE_VALUES.includes( value );
47
+ }
48
+
31
49
  function serializeRule( { selector, declarations } ) {
32
50
  return `${ selector } {
33
51
  ${ Object.entries( declarations )
@@ -60,6 +78,7 @@ export function getChildLayoutStyleRules( {
60
78
  columnSpan,
61
79
  rowSpan,
62
80
  } = effectiveLayout;
81
+ const baseSelfStretch = layout.selfStretch;
63
82
  const { columnCount, minimumColumnWidth } = parentLayout;
64
83
  const rules = [];
65
84
 
@@ -69,10 +88,30 @@ export function getChildLayoutStyleRules( {
69
88
  hasViewportOverride( 'selfStretch' ) ||
70
89
  hasViewportOverride( 'flexSize' )
71
90
  ) {
72
- if ( selfStretch === 'fixed' && flexSize ) {
91
+ if (
92
+ hasViewportOverrides &&
93
+ ( selfStretch === FLEX_CHILD_LAYOUT_VALUES.fit ||
94
+ selfStretch === FLEX_CHILD_LAYOUT_VALUES.grow ) &&
95
+ isFlexSizeValue( baseSelfStretch ) &&
96
+ layout.flexSize
97
+ ) {
98
+ declarations[ 'flex-basis' ] = 'unset';
99
+ if ( baseSelfStretch === FLEX_CHILD_LAYOUT_VALUES.fixed ) {
100
+ declarations[ 'flex-shrink' ] = 'unset';
101
+ }
102
+ }
103
+ if ( isFlexSizeValue( selfStretch ) && flexSize ) {
73
104
  declarations[ 'flex-basis' ] = flexSize;
105
+ if ( selfStretch === FLEX_CHILD_LAYOUT_VALUES.fixed ) {
106
+ declarations[ 'flex-shrink' ] = '0';
107
+ } else if (
108
+ hasViewportOverrides &&
109
+ baseSelfStretch === FLEX_CHILD_LAYOUT_VALUES.fixed
110
+ ) {
111
+ declarations[ 'flex-shrink' ] = 'unset';
112
+ }
74
113
  declarations[ 'box-sizing' ] = 'border-box';
75
- } else if ( selfStretch === 'fill' ) {
114
+ } else if ( selfStretch === FLEX_CHILD_LAYOUT_VALUES.grow ) {
76
115
  declarations[ 'flex-grow' ] = '1';
77
116
  }
78
117
  }
@@ -50,26 +50,25 @@ export function ListViewPanel( { clientId, name } ) {
50
50
  useDispatch( blockEditorStore )
51
51
  );
52
52
 
53
- const isEnabled = hasListViewSupport( name );
54
- const { hasChildren, isNestedListView } = useSelect(
53
+ const { isEnabled, hasChildren, isNestedListView } = useSelect(
55
54
  ( select ) => {
56
- const { getBlockCount, getBlockParents, getBlockName } =
57
- select( blockEditorStore );
55
+ const {
56
+ getBlockCount,
57
+ getBlockParents,
58
+ shouldRenderBlockListView,
59
+ } = unlock( select( blockEditorStore ) );
58
60
 
59
61
  // Avoid showing List Views for both parent and child blocks that have support.
60
62
  // In this situation the parent will show the child in its list already.
61
63
  // Search parents to see if there's one that also has support, and if so skip rendering.
62
64
  // This matches closely the logic in the `BlockCard` component.
63
65
  const parents = getBlockParents( clientId, false );
64
- const _isNestedListView = parents.find( ( parentId ) => {
65
- const parentName = getBlockName( parentId );
66
- return (
67
- parentName === 'core/navigation' ||
68
- hasBlockSupport( parentName, 'listView' )
69
- );
70
- } );
66
+ const _isNestedListView = parents.find( ( parentId ) =>
67
+ shouldRenderBlockListView( parentId )
68
+ );
71
69
 
72
70
  return {
71
+ isEnabled: shouldRenderBlockListView( clientId ),
73
72
  hasChildren: !! getBlockCount( clientId ),
74
73
  isNestedListView: _isNestedListView,
75
74
  };
@@ -29,6 +29,7 @@ import {
29
29
  DIMENSIONS_SUPPORT_KEY,
30
30
  SPACING_SUPPORT_KEY,
31
31
  DimensionsPanel,
32
+ isExplicitAspectRatio,
32
33
  } from './dimensions';
33
34
  import {
34
35
  cleanEmptyObject,
@@ -134,6 +135,72 @@ function getStateFallbackBorderStyles( stateStyles ) {
134
135
  return cleanEmptyObject( { border: cleanEmptyObject( fallbackBorder ) } );
135
136
  }
136
137
 
138
+ /**
139
+ * Returns background reset CSS for a state that sets a solid background color.
140
+ *
141
+ * When a state sets `color.background` (a solid color) without also setting a
142
+ * gradient (`color.gradient` or `background.gradient`), any gradient applied to
143
+ * the default state via an inline `background` shorthand or `background-image`
144
+ * declaration must be explicitly cleared. Without this, the gradient image layer
145
+ * remains visible even though the solid hover color wins `background-color`.
146
+ *
147
+ * @param {Object} stateStyles State style object.
148
+ * @param {string} selector CSS selector for the generated style.
149
+ * @return {string|undefined} CSS string with background-image reset, or undefined.
150
+ */
151
+ function getStateBackgroundResetCSS( stateStyles, selector ) {
152
+ const hasSolidBackground = !! stateStyles?.color?.background;
153
+
154
+ if ( ! hasSolidBackground ) {
155
+ return undefined;
156
+ }
157
+
158
+ const hasColorGradient = !! stateStyles?.color?.gradient;
159
+ const hasBackgroundGradient =
160
+ !! stateStyles?.background?.gradient ||
161
+ !! stateStyles?.background?.backgroundImage;
162
+
163
+ if ( hasColorGradient || hasBackgroundGradient ) {
164
+ return undefined;
165
+ }
166
+
167
+ const declaration = 'background-image: unset !important';
168
+ return selector
169
+ ? `${ selector } { ${ declaration }; }`
170
+ : `${ declaration };`;
171
+ }
172
+
173
+ /**
174
+ * Returns fallback dimension styles that keep state styles aligned with the
175
+ * default dimensions block-support output.
176
+ *
177
+ * @param {Object} stateStyles State style object.
178
+ * @return {Object|undefined} Style object containing fallback dimension styles.
179
+ */
180
+ function getStateFallbackDimensionStyles( stateStyles ) {
181
+ const dimensions = stateStyles?.dimensions;
182
+ if ( ! dimensions ) {
183
+ return undefined;
184
+ }
185
+
186
+ if ( isExplicitAspectRatio( dimensions.aspectRatio ) ) {
187
+ return {
188
+ dimensions: {
189
+ minHeight: 'unset',
190
+ height: 'unset',
191
+ },
192
+ };
193
+ }
194
+
195
+ if ( dimensions.minHeight || dimensions.height ) {
196
+ return {
197
+ dimensions: {
198
+ aspectRatio: 'unset',
199
+ },
200
+ };
201
+ }
202
+ }
203
+
137
204
  /**
138
205
  * Generates CSS for a block instance state style object.
139
206
  *
@@ -146,14 +213,25 @@ function getStateFallbackBorderStyles( stateStyles ) {
146
213
  * @return {string} Generated stylesheet.
147
214
  */
148
215
  export function getStateStylesCSS( stateStyles, selector ) {
149
- const css = compileCSS( stateStyles, { selector } );
216
+ const fallbackDimensionStyles =
217
+ getStateFallbackDimensionStyles( stateStyles );
218
+ const stylesWithDimensionFallbacks = fallbackDimensionStyles
219
+ ? mergeStyleObjects( stateStyles, fallbackDimensionStyles )
220
+ : stateStyles;
221
+ const css = compileCSS( stylesWithDimensionFallbacks, { selector } );
150
222
  const importantCSS = css ? css.replace( /;/g, ' !important;' ) : undefined;
151
223
  const fallbackBorderStyles = getStateFallbackBorderStyles( stateStyles );
152
224
  const fallbackCSS = fallbackBorderStyles
153
225
  ? compileCSS( fallbackBorderStyles, { selector } )
154
226
  : undefined;
227
+ const backgroundResetCSS = getStateBackgroundResetCSS(
228
+ stateStyles,
229
+ selector
230
+ );
155
231
 
156
- return [ importantCSS, fallbackCSS ].filter( Boolean ).join( '\n' );
232
+ return [ importantCSS, fallbackCSS, backgroundResetCSS ]
233
+ .filter( Boolean )
234
+ .join( '\n' );
157
235
  }
158
236
 
159
237
  function isPlainObject( value ) {
@@ -1,6 +1,16 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ getBlockType,
6
+ registerBlockType,
7
+ unregisterBlockType,
8
+ } from '@wordpress/blocks';
9
+
1
10
  /**
2
11
  * Internal dependencies
3
12
  */
13
+ import dimensions from '../dimensions';
4
14
  import { getDimensionsClassesAndStyles } from '../use-dimensions-props';
5
15
 
6
16
  describe( 'getDimensionsClassesAndStyles', () => {
@@ -126,3 +136,64 @@ describe( 'getDimensionsClassesAndStyles', () => {
126
136
  } );
127
137
  } );
128
138
  } );
139
+
140
+ describe( 'useBlockProps', () => {
141
+ const blockName = 'test/dimensions-with-aspect-ratio';
142
+
143
+ afterEach( () => {
144
+ if ( getBlockType( blockName ) ) {
145
+ unregisterBlockType( blockName );
146
+ }
147
+ } );
148
+
149
+ const registerDimensionsBlock = () =>
150
+ registerBlockType( blockName, {
151
+ apiVersion: 3,
152
+ title: 'Dimensions with aspect ratio',
153
+ category: 'text',
154
+ supports: {
155
+ dimensions: {
156
+ aspectRatio: true,
157
+ },
158
+ },
159
+ } );
160
+
161
+ it( 'unsets height styles when aspect ratio is explicit', () => {
162
+ registerDimensionsBlock();
163
+
164
+ expect(
165
+ dimensions.useBlockProps( {
166
+ name: blockName,
167
+ style: {
168
+ dimensions: {
169
+ aspectRatio: '16/9',
170
+ },
171
+ },
172
+ } )
173
+ ).toEqual( {
174
+ className: 'has-aspect-ratio',
175
+ style: {
176
+ minHeight: 'unset',
177
+ height: 'unset',
178
+ },
179
+ } );
180
+ } );
181
+
182
+ it( 'does not unset height styles when aspect ratio is the default', () => {
183
+ registerDimensionsBlock();
184
+
185
+ expect(
186
+ dimensions.useBlockProps( {
187
+ name: blockName,
188
+ style: {
189
+ dimensions: {
190
+ aspectRatio: 'auto',
191
+ },
192
+ },
193
+ } )
194
+ ).toEqual( {
195
+ className: '',
196
+ style: {},
197
+ } );
198
+ } );
199
+ } );