@wordpress/block-editor 15.20.0 → 15.21.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 (35) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-list/use-block-props/index.cjs +1 -1
  3. package/build/components/block-list/use-block-props/index.cjs.map +2 -2
  4. package/build/components/child-layout-control/index.cjs +10 -5
  5. package/build/components/child-layout-control/index.cjs.map +2 -2
  6. package/build/components/global-styles/dimensions-panel.cjs +5 -4
  7. package/build/components/global-styles/dimensions-panel.cjs.map +2 -2
  8. package/build/components/iframe/index.cjs +3 -0
  9. package/build/components/iframe/index.cjs.map +2 -2
  10. package/build/components/inserter/media-tab/utils.cjs +1 -1
  11. package/build/components/inserter/media-tab/utils.cjs.map +2 -2
  12. package/build/store/private-selectors.cjs +6 -0
  13. package/build/store/private-selectors.cjs.map +2 -2
  14. package/build-module/components/block-list/use-block-props/index.mjs +1 -1
  15. package/build-module/components/block-list/use-block-props/index.mjs.map +2 -2
  16. package/build-module/components/child-layout-control/index.mjs +10 -5
  17. package/build-module/components/child-layout-control/index.mjs.map +2 -2
  18. package/build-module/components/global-styles/dimensions-panel.mjs +8 -5
  19. package/build-module/components/global-styles/dimensions-panel.mjs.map +2 -2
  20. package/build-module/components/iframe/index.mjs +3 -0
  21. package/build-module/components/iframe/index.mjs.map +2 -2
  22. package/build-module/components/inserter/media-tab/utils.mjs +1 -1
  23. package/build-module/components/inserter/media-tab/utils.mjs.map +2 -2
  24. package/build-module/store/private-selectors.mjs +5 -0
  25. package/build-module/store/private-selectors.mjs.map +2 -2
  26. package/package.json +41 -41
  27. package/src/components/block-list/use-block-props/index.js +1 -1
  28. package/src/components/child-layout-control/index.js +15 -8
  29. package/src/components/child-layout-control/test/index.js +126 -0
  30. package/src/components/colors/test/with-colors.js +1 -1
  31. package/src/components/global-styles/dimensions-panel.js +12 -4
  32. package/src/components/iframe/index.js +3 -0
  33. package/src/components/inserter/media-tab/utils.js +1 -1
  34. package/src/store/private-selectors.js +17 -0
  35. package/src/store/test/private-selectors.js +53 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/block-editor",
3
- "version": "15.20.0",
3
+ "version": "15.21.0",
4
4
  "description": "Generic block editor.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -61,43 +61,43 @@
61
61
  ],
62
62
  "dependencies": {
63
63
  "@react-spring/web": "^9.4.5",
64
- "@wordpress/a11y": "^4.47.0",
65
- "@wordpress/base-styles": "^9.0.0",
66
- "@wordpress/blob": "^4.47.0",
67
- "@wordpress/block-serialization-default-parser": "^5.47.0",
68
- "@wordpress/blocks": "^15.20.0",
69
- "@wordpress/commands": "^1.47.0",
70
- "@wordpress/components": "^34.0.0",
71
- "@wordpress/compose": "^8.0.0",
72
- "@wordpress/data": "^10.47.0",
73
- "@wordpress/dataviews": "^15.0.0",
74
- "@wordpress/date": "^5.47.0",
75
- "@wordpress/deprecated": "^4.47.0",
76
- "@wordpress/dom": "^4.47.0",
77
- "@wordpress/element": "^7.0.0",
78
- "@wordpress/escape-html": "^3.47.0",
79
- "@wordpress/global-styles-engine": "^1.14.0",
80
- "@wordpress/hooks": "^4.47.0",
81
- "@wordpress/html-entities": "^4.47.0",
82
- "@wordpress/i18n": "^6.20.0",
83
- "@wordpress/icons": "^13.2.0",
84
- "@wordpress/image-cropper": "^1.11.0",
85
- "@wordpress/interactivity": "^6.47.0",
86
- "@wordpress/is-shallow-equal": "^5.47.0",
87
- "@wordpress/keyboard-shortcuts": "^5.47.0",
88
- "@wordpress/keycodes": "^4.47.0",
89
- "@wordpress/notices": "^5.47.0",
90
- "@wordpress/preferences": "^4.47.0",
91
- "@wordpress/priority-queue": "^3.47.0",
92
- "@wordpress/private-apis": "^1.47.0",
93
- "@wordpress/rich-text": "^7.47.0",
94
- "@wordpress/style-engine": "^2.47.0",
95
- "@wordpress/token-list": "^3.47.0",
96
- "@wordpress/ui": "^0.14.0",
97
- "@wordpress/upload-media": "^0.32.0",
98
- "@wordpress/url": "^4.47.0",
99
- "@wordpress/warning": "^3.47.0",
100
- "@wordpress/wordcount": "^4.47.0",
64
+ "@wordpress/a11y": "^4.48.0",
65
+ "@wordpress/base-styles": "^9.1.0",
66
+ "@wordpress/blob": "^4.48.0",
67
+ "@wordpress/block-serialization-default-parser": "^5.48.0",
68
+ "@wordpress/blocks": "^15.21.0",
69
+ "@wordpress/commands": "^1.48.0",
70
+ "@wordpress/components": "^35.0.0",
71
+ "@wordpress/compose": "^8.1.0",
72
+ "@wordpress/data": "^10.48.0",
73
+ "@wordpress/dataviews": "^16.0.0",
74
+ "@wordpress/date": "^5.48.0",
75
+ "@wordpress/deprecated": "^4.48.0",
76
+ "@wordpress/dom": "^4.48.0",
77
+ "@wordpress/element": "^8.0.0",
78
+ "@wordpress/escape-html": "^3.48.0",
79
+ "@wordpress/global-styles-engine": "^1.15.0",
80
+ "@wordpress/hooks": "^4.48.0",
81
+ "@wordpress/html-entities": "^4.48.0",
82
+ "@wordpress/i18n": "^6.21.0",
83
+ "@wordpress/icons": "^13.3.0",
84
+ "@wordpress/image-cropper": "^1.12.0",
85
+ "@wordpress/interactivity": "^6.48.0",
86
+ "@wordpress/is-shallow-equal": "^5.48.0",
87
+ "@wordpress/keyboard-shortcuts": "^5.48.0",
88
+ "@wordpress/keycodes": "^4.48.0",
89
+ "@wordpress/notices": "^5.48.0",
90
+ "@wordpress/preferences": "^4.48.0",
91
+ "@wordpress/priority-queue": "^3.48.0",
92
+ "@wordpress/private-apis": "^1.48.0",
93
+ "@wordpress/rich-text": "^7.48.0",
94
+ "@wordpress/style-engine": "^2.48.0",
95
+ "@wordpress/token-list": "^3.48.0",
96
+ "@wordpress/ui": "^0.15.0",
97
+ "@wordpress/upload-media": "^0.33.0",
98
+ "@wordpress/url": "^4.48.0",
99
+ "@wordpress/warning": "^3.48.0",
100
+ "@wordpress/wordcount": "^4.48.0",
101
101
  "change-case": "^4.1.2",
102
102
  "clsx": "^2.1.1",
103
103
  "colord": "^2.7.0",
@@ -118,11 +118,11 @@
118
118
  "deep-freeze": "0.0.1"
119
119
  },
120
120
  "peerDependencies": {
121
- "react": "^19.2.4",
122
- "react-dom": "^19.2.4"
121
+ "react": "^18.0.0",
122
+ "react-dom": "^18.0.0"
123
123
  },
124
124
  "publishConfig": {
125
125
  "access": "public"
126
126
  },
127
- "gitHead": "d653c5fd6161571a0c2ebde28553d6e25624eacc"
127
+ "gitHead": "e7856693aeb4e2522d13608cd32c994e4a97cb9c"
128
128
  }
@@ -185,7 +185,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
185
185
  'data-block': clientId,
186
186
  'data-type': name,
187
187
  'data-title': blockTitle,
188
- inert: isSubtreeDisabled ? true : undefined,
188
+ inert: isSubtreeDisabled ? 'true' : undefined,
189
189
  className: clsx(
190
190
  'block-editor-block-list__block',
191
191
  {
@@ -40,13 +40,14 @@ function helpText( selfStretch, parentLayout ) {
40
40
  /**
41
41
  * Form to edit the child layout value.
42
42
  *
43
- * @param {Object} props Props.
44
- * @param {Object} props.value The child layout value.
45
- * @param {Function} props.onChange Function to update the child layout value.
46
- * @param {Object} props.parentLayout The parent layout value.
43
+ * @param {Object} props Props.
44
+ * @param {Object} props.value The child layout value.
45
+ * @param {Function} props.onChange Function to update the child layout value.
46
+ * @param {Object} props.parentLayout The parent layout value.
47
47
  *
48
- * @param {boolean} props.isShownByDefault
49
- * @param {string} props.panelId
48
+ * @param {boolean} props.isShownByDefault Whether the control is shown by default.
49
+ * @param {string} props.panelId The panel ID.
50
+ * @param {boolean} props.showGridSpanDefaults Whether unset grid span controls should show default values.
50
51
  * @return {Element} child layout edit element.
51
52
  */
52
53
  export default function ChildLayoutControl( {
@@ -55,6 +56,7 @@ export default function ChildLayoutControl( {
55
56
  parentLayout,
56
57
  isShownByDefault,
57
58
  panelId,
59
+ showGridSpanDefaults = true,
58
60
  } ) {
59
61
  const {
60
62
  type: parentType,
@@ -80,6 +82,7 @@ export default function ChildLayoutControl( {
80
82
  parentLayout={ parentLayout }
81
83
  isShownByDefault={ isShownByDefault }
82
84
  panelId={ panelId }
85
+ showGridSpanDefaults={ showGridSpanDefaults }
83
86
  />
84
87
  );
85
88
  }
@@ -206,6 +209,7 @@ function GridControls( {
206
209
  parentLayout,
207
210
  isShownByDefault,
208
211
  panelId,
212
+ showGridSpanDefaults,
209
213
  } ) {
210
214
  const { columnStart, rowStart, columnSpan, rowSpan } = childLayout;
211
215
  const { columnCount, rowCount } = parentLayout ?? {};
@@ -243,6 +247,9 @@ function GridControls( {
243
247
  window.__experimentalEnableGridInteractivity && rowCount
244
248
  ? rowCount - ( rowStart ?? 1 ) + 1
245
249
  : undefined;
250
+ const columnSpanValue =
251
+ columnSpan ?? ( showGridSpanDefaults ? 1 : undefined );
252
+ const rowSpanValue = rowSpan ?? ( showGridSpanDefaults ? 1 : undefined );
246
253
 
247
254
  return (
248
255
  <>
@@ -274,7 +281,7 @@ function GridControls( {
274
281
  columnSpan: constrainedValue,
275
282
  } );
276
283
  } }
277
- value={ columnSpan ?? 1 }
284
+ value={ columnSpanValue }
278
285
  min={ 1 }
279
286
  max={ maxColumnSpan }
280
287
  />
@@ -298,7 +305,7 @@ function GridControls( {
298
305
  rowSpan: constrainedValue,
299
306
  } );
300
307
  } }
301
- value={ rowSpan ?? 1 }
308
+ value={ rowSpanValue }
302
309
  min={ 1 }
303
310
  max={ maxRowSpan }
304
311
  />
@@ -0,0 +1,126 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render, screen } from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { __experimentalToolsPanel as ToolsPanel } from '@wordpress/components';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import ChildLayoutControl from '../';
16
+
17
+ jest.mock( '@wordpress/data/src/components/use-select', () =>
18
+ jest.fn( ( mapSelect ) => {
19
+ if ( typeof mapSelect === 'function' ) {
20
+ return mapSelect( () => ( {
21
+ getBlockRootClientId: () => 'root-client-id',
22
+ } ) );
23
+ }
24
+
25
+ return {
26
+ getBlockAttributes: () => ( {} ),
27
+ getBlockOrder: () => [],
28
+ };
29
+ } )
30
+ );
31
+
32
+ jest.mock( '@wordpress/data/src/components/use-dispatch', () => ( {
33
+ useDispatch: () => ( {
34
+ __unstableMarkNextChangeAsNotPersistent: jest.fn(),
35
+ moveBlocksToPosition: jest.fn(),
36
+ } ),
37
+ } ) );
38
+
39
+ describe( 'ChildLayoutControl', () => {
40
+ const baseProps = {
41
+ onChange: jest.fn(),
42
+ parentLayout: {
43
+ type: 'grid',
44
+ },
45
+ isShownByDefault: true,
46
+ panelId: 'client-id',
47
+ };
48
+
49
+ afterEach( () => {
50
+ jest.clearAllMocks();
51
+ } );
52
+
53
+ function renderControl( props ) {
54
+ return render(
55
+ <ToolsPanel
56
+ label="Dimensions"
57
+ resetAll={ jest.fn() }
58
+ panelId="client-id"
59
+ >
60
+ <ChildLayoutControl { ...baseProps } { ...props } />
61
+ </ToolsPanel>
62
+ );
63
+ }
64
+
65
+ it( 'shows default grid span values for unset span controls by default', () => {
66
+ renderControl( { value: {} } );
67
+
68
+ expect(
69
+ screen.getByRole( 'spinbutton', { name: 'Column span' } )
70
+ ).toHaveValue( 1 );
71
+ expect(
72
+ screen.getByRole( 'spinbutton', { name: 'Row span' } )
73
+ ).toHaveValue( 1 );
74
+ } );
75
+
76
+ it( 'shows empty grid span values when defaults are hidden', () => {
77
+ renderControl( { value: {}, showGridSpanDefaults: false } );
78
+
79
+ expect(
80
+ screen.getByRole( 'spinbutton', { name: 'Column span' } )
81
+ ).toHaveValue( null );
82
+ expect(
83
+ screen.getByRole( 'spinbutton', { name: 'Row span' } )
84
+ ).toHaveValue( null );
85
+ } );
86
+
87
+ it( 'shows explicitly set grid span values when defaults are hidden', () => {
88
+ renderControl( {
89
+ value: {
90
+ columnSpan: 1,
91
+ rowSpan: 2,
92
+ },
93
+ showGridSpanDefaults: false,
94
+ } );
95
+
96
+ expect(
97
+ screen.getByRole( 'spinbutton', { name: 'Column span' } )
98
+ ).toHaveValue( 1 );
99
+ expect(
100
+ screen.getByRole( 'spinbutton', { name: 'Row span' } )
101
+ ).toHaveValue( 2 );
102
+ } );
103
+
104
+ it( 'sets a numeric span when entering 1 into an empty span control', async () => {
105
+ const user = userEvent.setup();
106
+ const onChange = jest.fn();
107
+
108
+ renderControl( {
109
+ value: {},
110
+ onChange,
111
+ showGridSpanDefaults: false,
112
+ } );
113
+
114
+ await user.type(
115
+ screen.getByRole( 'spinbutton', { name: 'Column span' } ),
116
+ '1'
117
+ );
118
+
119
+ expect( onChange ).toHaveBeenCalledWith( {
120
+ columnStart: undefined,
121
+ rowStart: undefined,
122
+ rowSpan: undefined,
123
+ columnSpan: 1,
124
+ } );
125
+ } );
126
+ } );
@@ -37,7 +37,7 @@ describe( 'createCustomColorsHOC', () => {
37
37
  colors: undefined,
38
38
  setBackgroundColor: expect.any( Function ),
39
39
  } ),
40
- undefined
40
+ expect.anything()
41
41
  );
42
42
  } );
43
43
 
@@ -32,6 +32,8 @@ import { setImmutably } from '../../utils/object';
32
32
  import {
33
33
  DEFAULT_BLOCK_STYLE_STATE,
34
34
  hasPseudoBlockStyleState,
35
+ isDefaultBlockStyleState,
36
+ hasViewportBlockStyleState,
35
37
  } from '../../hooks/block-style-state';
36
38
 
37
39
  const AXIAL_SIDES = [ 'horizontal', 'vertical' ];
@@ -51,7 +53,7 @@ export function useHasDimensionsPanel(
51
53
  hasMinHeight( settings ) ||
52
54
  hasMinWidth( settings ) ||
53
55
  hasWidth( settings ) ||
54
- hasAspectRatio( settings ) ||
56
+ hasAspectRatio( settings, styleState ) ||
55
57
  hasChildLayout( settings, styleState ) )
56
58
  );
57
59
  }
@@ -92,8 +94,11 @@ function hasWidth( settings ) {
92
94
  return settings?.dimensions?.width;
93
95
  }
94
96
 
95
- function hasAspectRatio( settings ) {
96
- return settings?.dimensions?.aspectRatio;
97
+ function hasAspectRatio( settings, styleState = DEFAULT_BLOCK_STYLE_STATE ) {
98
+ return (
99
+ isDefaultBlockStyleState( styleState ) &&
100
+ settings?.dimensions?.aspectRatio
101
+ );
97
102
  }
98
103
 
99
104
  function hasChildLayout( settings, styleState = DEFAULT_BLOCK_STYLE_STATE ) {
@@ -484,7 +489,7 @@ export default function DimensionsPanel( {
484
489
  const hasWidthValue = () => !! value?.dimensions?.width;
485
490
 
486
491
  // Aspect Ratio
487
- const showAspectRatioControl = hasAspectRatio( settings );
492
+ const showAspectRatioControl = hasAspectRatio( settings, styleState );
488
493
  const aspectRatioValue = decodeValue(
489
494
  inheritedValue?.dimensions?.aspectRatio
490
495
  );
@@ -737,6 +742,9 @@ export default function DimensionsPanel( {
737
742
  onChange={ setChildLayout }
738
743
  parentLayout={ settings?.parentLayout }
739
744
  panelId={ panelId }
745
+ showGridSpanDefaults={
746
+ ! hasViewportBlockStyleState( styleState )
747
+ }
740
748
  isShownByDefault={
741
749
  defaultControls.childLayout ??
742
750
  DEFAULT_CONTROLS.childLayout
@@ -125,6 +125,9 @@ function getIframeSrc( resolvedAssets ) {
125
125
  ${ resolvedAssets.styles ?? '' }
126
126
  ${ resolvedAssets.scripts ?? '' }
127
127
  </head>
128
+ <body>
129
+ <script>document.currentScript.parentElement.remove()</script>
130
+ </body>
128
131
  </html>`;
129
132
 
130
133
  src = URL.createObjectURL( new Blob( [ html ], { type: 'text/html' } ) );
@@ -34,7 +34,7 @@ export function getBlockAndPreviewFromMedia( media, mediaType ) {
34
34
  src={ media.previewUrl || mediaSrc }
35
35
  alt={ alt }
36
36
  controls={ mediaType === 'audio' ? true : undefined }
37
- inert
37
+ inert="true"
38
38
  onError={ ( { currentTarget } ) => {
39
39
  // Fall back to the media source if the preview cannot be loaded.
40
40
  if ( currentTarget.src === media.previewUrl ) {
@@ -1079,6 +1079,23 @@ export function getSelectedBlockStyleState( state, clientId ) {
1079
1079
  return state.selectedBlockStyleState.value ?? DEFAULT_BLOCK_STYLE_STATE;
1080
1080
  }
1081
1081
 
1082
+ /**
1083
+ * Returns whether a non-default style state is selected for a block.
1084
+ *
1085
+ * @param {Object} state Global application state.
1086
+ * @param {string} clientId The block client ID.
1087
+ *
1088
+ * @return {boolean} Whether a non-default block style state is selected.
1089
+ */
1090
+ export function hasSelectedStyleState( state, clientId ) {
1091
+ const selectedState = getSelectedBlockStyleState( state, clientId );
1092
+
1093
+ return (
1094
+ selectedState.viewport !== DEFAULT_BLOCK_STYLE_STATE.viewport ||
1095
+ selectedState.pseudo !== DEFAULT_BLOCK_STYLE_STATE.pseudo
1096
+ );
1097
+ }
1098
+
1082
1099
  /**
1083
1100
  * Returns whether the selected style state is shown on the canvas.
1084
1101
  *
@@ -25,6 +25,7 @@ import {
25
25
  isSectionBlock,
26
26
  getParentSectionBlock,
27
27
  getSelectedBlockStyleState,
28
+ hasSelectedStyleState,
28
29
  isSelectedBlockStyleStateShownOnCanvas,
29
30
  } from '../private-selectors';
30
31
  import { getBlockEditingMode } from '../selectors';
@@ -126,6 +127,58 @@ describe( 'private selectors', () => {
126
127
  } );
127
128
  } );
128
129
 
130
+ describe( 'hasSelectedStyleState', () => {
131
+ it( 'returns false when the block has no selected state', () => {
132
+ const state = {};
133
+
134
+ expect( hasSelectedStyleState( state, 'client-1' ) ).toBe( false );
135
+ } );
136
+
137
+ it( 'returns false when another block has the selected state', () => {
138
+ const state = {
139
+ selectedBlockStyleState: {
140
+ clientId: 'client-2',
141
+ value: { viewport: 'default', pseudo: ':hover' },
142
+ },
143
+ };
144
+
145
+ expect( hasSelectedStyleState( state, 'client-1' ) ).toBe( false );
146
+ } );
147
+
148
+ it( 'returns true when a viewport state is selected', () => {
149
+ const state = {
150
+ selectedBlockStyleState: {
151
+ clientId: 'client-1',
152
+ value: { viewport: 'mobile', pseudo: 'default' },
153
+ },
154
+ };
155
+
156
+ expect( hasSelectedStyleState( state, 'client-1' ) ).toBe( true );
157
+ } );
158
+
159
+ it( 'returns true when a pseudo state is selected', () => {
160
+ const state = {
161
+ selectedBlockStyleState: {
162
+ clientId: 'client-1',
163
+ value: { viewport: 'default', pseudo: ':hover' },
164
+ },
165
+ };
166
+
167
+ expect( hasSelectedStyleState( state, 'client-1' ) ).toBe( true );
168
+ } );
169
+
170
+ it( 'returns true when viewport and pseudo states are selected', () => {
171
+ const state = {
172
+ selectedBlockStyleState: {
173
+ clientId: 'client-1',
174
+ value: { viewport: 'mobile', pseudo: ':hover' },
175
+ },
176
+ };
177
+
178
+ expect( hasSelectedStyleState( state, 'client-1' ) ).toBe( true );
179
+ } );
180
+ } );
181
+
129
182
  describe( 'isSelectedBlockStyleStateShownOnCanvas', () => {
130
183
  it( 'returns true when the block has no canvas preview state', () => {
131
184
  const state = {};