@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
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { getChildLayoutStyleRules } from '../layout-child';
5
+
6
+ describe( 'layout child', () => {
7
+ describe( 'getChildLayoutStyleRules()', () => {
8
+ it( 'preserves legacy fixed sizing as shrinkable max width', () => {
9
+ expect(
10
+ getChildLayoutStyleRules( {
11
+ selector: '.wp-container-content-test',
12
+ layout: {
13
+ selfStretch: 'fixed',
14
+ flexSize: '320px',
15
+ },
16
+ } )
17
+ ).toEqual( [
18
+ {
19
+ selector: '.wp-container-content-test',
20
+ declarations: {
21
+ 'flex-basis': '320px',
22
+ 'box-sizing': 'border-box',
23
+ },
24
+ },
25
+ ] );
26
+ } );
27
+
28
+ it( 'adds flex-shrink for fixedNoShrink sizing', () => {
29
+ expect(
30
+ getChildLayoutStyleRules( {
31
+ selector: '.wp-container-content-test',
32
+ layout: {
33
+ selfStretch: 'fixedNoShrink',
34
+ flexSize: '320px',
35
+ },
36
+ } )
37
+ ).toEqual( [
38
+ {
39
+ selector: '.wp-container-content-test',
40
+ declarations: {
41
+ 'flex-basis': '320px',
42
+ 'flex-shrink': '0',
43
+ 'box-sizing': 'border-box',
44
+ },
45
+ },
46
+ ] );
47
+ } );
48
+
49
+ it( 'allows viewport overrides to switch fixedNoShrink to max', () => {
50
+ expect(
51
+ getChildLayoutStyleRules( {
52
+ selector: '.wp-container-content-test',
53
+ layout: {
54
+ selfStretch: 'fixedNoShrink',
55
+ flexSize: '320px',
56
+ },
57
+ viewportOverrides: {
58
+ selfStretch: 'fixed',
59
+ },
60
+ } )
61
+ ).toEqual( [
62
+ {
63
+ selector: '.wp-container-content-test',
64
+ declarations: {
65
+ 'flex-basis': '320px',
66
+ 'flex-shrink': 'unset',
67
+ 'box-sizing': 'border-box',
68
+ },
69
+ },
70
+ ] );
71
+ } );
72
+
73
+ it( 'allows viewport overrides to switch fixedNoShrink to fit', () => {
74
+ expect(
75
+ getChildLayoutStyleRules( {
76
+ selector: '.wp-container-content-test',
77
+ layout: {
78
+ selfStretch: 'fixedNoShrink',
79
+ flexSize: '320px',
80
+ },
81
+ viewportOverrides: {
82
+ selfStretch: 'fit',
83
+ },
84
+ } )
85
+ ).toEqual( [
86
+ {
87
+ selector: '.wp-container-content-test',
88
+ declarations: {
89
+ 'flex-basis': 'unset',
90
+ 'flex-shrink': 'unset',
91
+ },
92
+ },
93
+ ] );
94
+ } );
95
+
96
+ it( 'allows viewport overrides to switch fixed to fit', () => {
97
+ expect(
98
+ getChildLayoutStyleRules( {
99
+ selector: '.wp-container-content-test',
100
+ layout: {
101
+ selfStretch: 'fixed',
102
+ flexSize: '320px',
103
+ },
104
+ viewportOverrides: {
105
+ selfStretch: 'fit',
106
+ },
107
+ } )
108
+ ).toEqual( [
109
+ {
110
+ selector: '.wp-container-content-test',
111
+ declarations: {
112
+ 'flex-basis': 'unset',
113
+ },
114
+ },
115
+ ] );
116
+ } );
117
+
118
+ it( 'allows viewport overrides to switch fixedNoShrink to grow', () => {
119
+ expect(
120
+ getChildLayoutStyleRules( {
121
+ selector: '.wp-container-content-test',
122
+ layout: {
123
+ selfStretch: 'fixedNoShrink',
124
+ flexSize: '320px',
125
+ },
126
+ viewportOverrides: {
127
+ selfStretch: 'fill',
128
+ },
129
+ } )
130
+ ).toEqual( [
131
+ {
132
+ selector: '.wp-container-content-test',
133
+ declarations: {
134
+ 'flex-basis': 'unset',
135
+ 'flex-shrink': 'unset',
136
+ 'flex-grow': '1',
137
+ },
138
+ },
139
+ ] );
140
+ } );
141
+
142
+ it( 'allows viewport overrides to switch fixed to grow', () => {
143
+ expect(
144
+ getChildLayoutStyleRules( {
145
+ selector: '.wp-container-content-test',
146
+ layout: {
147
+ selfStretch: 'fixed',
148
+ flexSize: '320px',
149
+ },
150
+ viewportOverrides: {
151
+ selfStretch: 'fill',
152
+ },
153
+ } )
154
+ ).toEqual( [
155
+ {
156
+ selector: '.wp-container-content-test',
157
+ declarations: {
158
+ 'flex-basis': 'unset',
159
+ 'flex-grow': '1',
160
+ },
161
+ },
162
+ ] );
163
+ } );
164
+ } );
165
+ } );
@@ -208,6 +208,98 @@ describe( 'getStateStylesCSS', () => {
208
208
  '.wp-block-test:hover { border-top-color: #0000ff !important; }\n.wp-block-test:hover { border-top-style: solid; }'
209
209
  );
210
210
  } );
211
+
212
+ it( 'adds background-image reset when state sets solid background-color', () => {
213
+ expect(
214
+ getStateStylesCSS(
215
+ {
216
+ color: {
217
+ background: '#ff0000',
218
+ },
219
+ },
220
+ '.wp-block-test:hover'
221
+ )
222
+ ).toBe(
223
+ '.wp-block-test:hover { background-color: #ff0000 !important; }\n.wp-block-test:hover { background-image: unset !important; }'
224
+ );
225
+ } );
226
+
227
+ it( 'does not add background-image reset when state also sets a legacy gradient', () => {
228
+ expect(
229
+ getStateStylesCSS(
230
+ {
231
+ color: {
232
+ background: '#ff0000',
233
+ gradient: 'linear-gradient(135deg, #ff0000, #0000ff)',
234
+ },
235
+ },
236
+ '.wp-block-test:hover'
237
+ )
238
+ ).toBe(
239
+ '.wp-block-test:hover { background: linear-gradient(135deg, #ff0000, #0000ff) !important; background-color: #ff0000 !important; }'
240
+ );
241
+ } );
242
+
243
+ it( 'does not add background-image reset when state also sets a modern gradient', () => {
244
+ expect(
245
+ getStateStylesCSS(
246
+ {
247
+ color: {
248
+ background: '#ff0000',
249
+ },
250
+ background: {
251
+ gradient: 'linear-gradient(135deg, #ff0000, #0000ff)',
252
+ },
253
+ },
254
+ '.wp-block-test:hover'
255
+ )
256
+ ).toBe(
257
+ '.wp-block-test:hover { background-color: #ff0000 !important; background-image: linear-gradient(135deg, #ff0000, #0000ff) !important; }'
258
+ );
259
+ } );
260
+
261
+ it( 'adds important fallback dimensions when aspect ratio is set', () => {
262
+ expect(
263
+ getStateStylesCSS(
264
+ {
265
+ dimensions: {
266
+ aspectRatio: '16/9',
267
+ },
268
+ },
269
+ '.wp-block-test'
270
+ )
271
+ ).toBe(
272
+ '.wp-block-test { height: unset !important; min-height: unset !important; aspect-ratio: 16/9 !important; }'
273
+ );
274
+ } );
275
+
276
+ it( 'does not add fallback dimensions when aspect ratio is the default', () => {
277
+ expect(
278
+ getStateStylesCSS(
279
+ {
280
+ dimensions: {
281
+ aspectRatio: 'auto',
282
+ },
283
+ },
284
+ '.wp-block-test'
285
+ )
286
+ ).toBe( '.wp-block-test { aspect-ratio: auto !important; }' );
287
+ } );
288
+
289
+ it( 'adds important fallback aspect ratio when height is set', () => {
290
+ expect(
291
+ getStateStylesCSS(
292
+ {
293
+ dimensions: {
294
+ height: '20rem',
295
+ },
296
+ },
297
+ '.wp-block-test'
298
+ )
299
+ ).toBe(
300
+ '.wp-block-test { height: 20rem !important; aspect-ratio: unset !important; }'
301
+ );
302
+ } );
211
303
  } );
212
304
 
213
305
  describe( 'getBlockStateStylesCSS', () => {
@@ -247,7 +339,7 @@ describe( 'getBlockStateStylesCSS', () => {
247
339
  }
248
340
  )
249
341
  ).toBe(
250
- '.wp-elements-abc123 .wp-block-button__link:hover { background-color: #ff00d0 !important; }\n.wp-elements-abc123:hover { width: 50% !important; }'
342
+ '.wp-elements-abc123 .wp-block-button__link:hover { background-color: #ff00d0 !important; }\n.wp-elements-abc123 .wp-block-button__link:hover { background-image: unset !important; }\n.wp-elements-abc123:hover { width: 50% !important; }'
251
343
  );
252
344
  } );
253
345
 
@@ -264,7 +356,7 @@ describe( 'getBlockStateStylesCSS', () => {
264
356
  }
265
357
  )
266
358
  ).toBe(
267
- '[data-block="client-id"] .wp-block-button__link { background-color: #ff00d0 !important; }\n[data-block="client-id"] { width: 50% !important; }'
359
+ '[data-block="client-id"] .wp-block-button__link { background-color: #ff00d0 !important; }\n[data-block="client-id"] .wp-block-button__link { background-image: unset !important; }\n[data-block="client-id"] { width: 50% !important; }'
268
360
  );
269
361
  } );
270
362
  } );
@@ -286,10 +378,24 @@ describe( 'getResponsiveStateCSSRules', () => {
286
378
  },
287
379
  },
288
380
  } );
381
+
382
+ registerBlockType( 'test/state-image', {
383
+ apiVersion: 3,
384
+ title: 'State Image',
385
+ category: 'media',
386
+ attributes: {},
387
+ edit: () => null,
388
+ save: () => null,
389
+ selectors: {
390
+ root: '.wp-block-test-state-image',
391
+ dimensions: '.wp-block-test-state-image img',
392
+ },
393
+ } );
289
394
  } );
290
395
 
291
396
  afterEach( () => {
292
397
  unregisterBlockType( 'test/state-button' );
398
+ unregisterBlockType( 'test/state-image' );
293
399
  } );
294
400
 
295
401
  it( 'generates media-query scoped root styles for viewport states', () => {
@@ -321,7 +427,23 @@ describe( 'getResponsiveStateCSSRules', () => {
321
427
  '.wp-elements-1'
322
428
  )
323
429
  ).toEqual( [
324
- '@media (width <= 480px){.wp-elements-1 .wp-block-button__link { background-color: #ff00d0 !important; }\n.wp-elements-1 { width: 50% !important; }}',
430
+ '@media (width <= 480px){.wp-elements-1 .wp-block-button__link { background-color: #ff00d0 !important; }\n.wp-elements-1 .wp-block-button__link { background-image: unset !important; }\n.wp-elements-1 { width: 50% !important; }}',
431
+ ] );
432
+ } );
433
+
434
+ it( 'outputs explicit fill object fit for viewport states', () => {
435
+ expect(
436
+ getResponsiveStateCSSRules(
437
+ {
438
+ mobile: {
439
+ dimensions: { objectFit: 'fill' },
440
+ },
441
+ },
442
+ 'test/state-image',
443
+ '.wp-elements-1'
444
+ )
445
+ ).toEqual( [
446
+ '@media (width <= 480px){.wp-elements-1 img { object-fit: fill !important; }}',
325
447
  ] );
326
448
  } );
327
449
 
@@ -339,7 +461,7 @@ describe( 'getResponsiveStateCSSRules', () => {
339
461
  '.wp-elements-1'
340
462
  )
341
463
  ).toEqual( [
342
- '@media (width <= 480px){.wp-elements-1:hover { background-color: black !important; }}',
464
+ '@media (width <= 480px){.wp-elements-1:hover { background-color: black !important; }\n.wp-elements-1:hover { background-image: unset !important; }}',
343
465
  ] );
344
466
  } );
345
467
 
@@ -16,6 +16,11 @@ import { PrivateListView } from './components/list-view';
16
16
  import InspectorControlsLastItem from './components/inspector-controls/last-item';
17
17
  import { useHasBlockToolbar } from './components/block-toolbar/use-has-block-toolbar';
18
18
  import { cleanEmptyObject, usePrivateStyleOverride } from './hooks/utils';
19
+ import {
20
+ getStyleForState,
21
+ isDefaultBlockStyleState,
22
+ setStyleForState,
23
+ } from './hooks/block-style-state';
19
24
  import BlockQuickNavigation from './components/block-quick-navigation';
20
25
  import { LayoutStyle } from './components/block-list/layout';
21
26
  import BlockManager from './components/block-manager';
@@ -98,6 +103,9 @@ lock( privateApis, {
98
103
  InspectorControlsLastItem,
99
104
  useHasBlockToolbar,
100
105
  cleanEmptyObject,
106
+ getStyleForState,
107
+ isDefaultBlockStyleState,
108
+ setStyleForState,
101
109
  usePrivateStyleOverride,
102
110
  BlockQuickNavigation,
103
111
  LayoutStyle,
@@ -14,6 +14,7 @@ import {
14
14
  getBlockOrder,
15
15
  getBlockParents,
16
16
  getBlockEditingMode,
17
+ getBlockListSettings,
17
18
  getSettings,
18
19
  canInsertBlockType,
19
20
  getBlockName,
@@ -1077,6 +1078,52 @@ export function getListViewExpandRevision( state ) {
1077
1078
  return state.listViewExpandRevision || 0;
1078
1079
  }
1079
1080
 
1081
+ /**
1082
+ * Returns whether a block instance participates in List View-specific UI for
1083
+ * its inner blocks.
1084
+ *
1085
+ * Intentionally private: this is the derived participation logic (block type
1086
+ * `listView` support and the `core/navigation` special case) shared by the List
1087
+ * View consumers. A `listView`-supporting block drops out when it has no inner
1088
+ * blocks and its `allowedBlocks` (`[]` or `false`) permits no block: the nested
1089
+ * List View panel would render no rows and no appender, so it is hidden rather
1090
+ * than shown empty. This is a signal, not a guarantee — a child naming this
1091
+ * block as its `parent` stays insertable regardless (see `canInsertBlockType`);
1092
+ * that edge case is accepted to keep the check cheap. Keeping the read internal
1093
+ * lets this computation evolve without a back-compat commitment.
1094
+ *
1095
+ * @param {Object} state Global application state.
1096
+ * @param {string} clientId Client ID of the block.
1097
+ *
1098
+ * @return {boolean} Whether the block participates in List View-specific UI.
1099
+ */
1100
+ export function shouldRenderBlockListView( state, clientId ) {
1101
+ const blockName = getBlockName( state, clientId );
1102
+
1103
+ // The navigation block always participates; its List View is core to how it
1104
+ // is edited, regardless of how its menu is locked or populated.
1105
+ if ( blockName === 'core/navigation' ) {
1106
+ return true;
1107
+ }
1108
+
1109
+ if ( ! hasBlockSupport( blockName, 'listView' ) ) {
1110
+ return false;
1111
+ }
1112
+
1113
+ // `allowedBlocks` permits no block when it is `[]` or `false`; an unset value
1114
+ // is unrestricted and is intentionally not matched.
1115
+ const allowedBlocks = getBlockListSettings(
1116
+ state,
1117
+ clientId
1118
+ )?.allowedBlocks;
1119
+ const isEmptyAndNoAllowedBlocks =
1120
+ getBlockOrder( state, clientId ).length === 0 &&
1121
+ ( allowedBlocks === false ||
1122
+ ( Array.isArray( allowedBlocks ) && allowedBlocks.length === 0 ) );
1123
+
1124
+ return ! isEmptyAndNoAllowedBlocks;
1125
+ }
1126
+
1080
1127
  /**
1081
1128
  * Returns the client IDs for the viewport modal, or null if
1082
1129
  * the modal is not open.
@@ -27,6 +27,7 @@ import {
27
27
  getSelectedBlockStyleState,
28
28
  hasSelectedStyleState,
29
29
  isSelectedBlockStyleStateShownOnCanvas,
30
+ shouldRenderBlockListView,
30
31
  } from '../private-selectors';
31
32
  import { getBlockEditingMode } from '../selectors';
32
33
  import { deviceTypeKey } from '../private-keys';
@@ -75,6 +76,143 @@ describe( 'private selectors', () => {
75
76
  } );
76
77
  } );
77
78
 
79
+ describe( 'shouldRenderBlockListView', () => {
80
+ const blockWithListViewSupport = 'core/test-list-view-support';
81
+ const blockWithoutListViewSupport = 'core/test-no-list-view-support';
82
+
83
+ const createState = (
84
+ blockName,
85
+ { allowedBlocks, innerBlocks = [] } = {}
86
+ ) => {
87
+ const blockListSettings = new Map();
88
+ if ( allowedBlocks !== undefined ) {
89
+ blockListSettings.set( 'client-1', { allowedBlocks } );
90
+ }
91
+
92
+ return {
93
+ blocks: {
94
+ byClientId: new Map( [
95
+ [ 'client-1', { name: blockName } ],
96
+ ] ),
97
+ order: new Map( [ [ 'client-1', innerBlocks ] ] ),
98
+ parents: new Map(),
99
+ },
100
+ blockListSettings,
101
+ };
102
+ };
103
+
104
+ beforeAll( () => {
105
+ registerBlockType( blockWithListViewSupport, {
106
+ apiVersion: 3,
107
+ title: 'List View support',
108
+ category: 'text',
109
+ supports: {
110
+ listView: true,
111
+ },
112
+ } );
113
+ registerBlockType( blockWithoutListViewSupport, {
114
+ apiVersion: 3,
115
+ title: 'No List View support',
116
+ category: 'text',
117
+ } );
118
+ } );
119
+
120
+ afterAll( () => {
121
+ unregisterBlockType( blockWithListViewSupport );
122
+ unregisterBlockType( blockWithoutListViewSupport );
123
+ } );
124
+
125
+ it( 'returns true for blocks with list view support', () => {
126
+ const state = createState( blockWithListViewSupport );
127
+
128
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
129
+ true
130
+ );
131
+ } );
132
+
133
+ it( 'returns false when empty and insertion is disallowed via `allowedBlocks: []`', () => {
134
+ const state = createState( blockWithListViewSupport, {
135
+ allowedBlocks: [],
136
+ } );
137
+
138
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
139
+ false
140
+ );
141
+ } );
142
+
143
+ it( 'returns false when empty and insertion is disallowed via `allowedBlocks: false`', () => {
144
+ const state = createState( blockWithListViewSupport, {
145
+ allowedBlocks: false,
146
+ } );
147
+
148
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
149
+ false
150
+ );
151
+ } );
152
+
153
+ it( 'returns true when empty but insertion is allowed', () => {
154
+ // e.g. a static, still-empty gallery: nothing yet, but the user can
155
+ // start inserting, so its List View stays available.
156
+ const state = createState( blockWithListViewSupport, {
157
+ allowedBlocks: [ 'core/image' ],
158
+ } );
159
+
160
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
161
+ true
162
+ );
163
+ } );
164
+
165
+ it( 'returns true when empty with no allowedBlocks restriction', () => {
166
+ const state = createState( blockWithListViewSupport );
167
+
168
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
169
+ true
170
+ );
171
+ } );
172
+
173
+ it( 'returns true when insertion is disallowed but the block still has inner blocks', () => {
174
+ const state = createState( blockWithListViewSupport, {
175
+ allowedBlocks: [],
176
+ innerBlocks: [ 'child-1' ],
177
+ } );
178
+
179
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
180
+ true
181
+ );
182
+ } );
183
+
184
+ it( 'does not grant list view support to unsupported block types', () => {
185
+ const state = createState( blockWithoutListViewSupport, {
186
+ allowedBlocks: [],
187
+ } );
188
+
189
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
190
+ false
191
+ );
192
+ } );
193
+
194
+ it( 'preserves the navigation block special case', () => {
195
+ const state = createState( 'core/navigation' );
196
+
197
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
198
+ true
199
+ );
200
+ } );
201
+
202
+ it( 'keeps navigation in list view even when empty and insertion is disallowed', () => {
203
+ // The navigation special case takes precedence over the
204
+ // `allowedBlocks` exclusion: even with nothing to show and no
205
+ // insertion allowed, navigation still participates in List View.
206
+ const state = createState( 'core/navigation', {
207
+ allowedBlocks: [],
208
+ } );
209
+
210
+ expect( shouldRenderBlockListView( state, 'client-1' ) ).toBe(
211
+ true
212
+ );
213
+ } );
214
+ } );
215
+
78
216
  describe( 'getSelectedBlockStyleState', () => {
79
217
  it( 'returns default when the block has no selected state', () => {
80
218
  const state = {};