@wordpress/block-editor 10.0.1 → 10.1.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 (105) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-list/index.js +19 -5
  3. package/build/components/block-list/index.js.map +1 -1
  4. package/build/components/block-list/use-block-props/index.js +7 -1
  5. package/build/components/block-list/use-block-props/index.js.map +1 -1
  6. package/build/components/block-list/use-in-between-inserter.js +10 -1
  7. package/build/components/block-list/use-in-between-inserter.js.map +1 -1
  8. package/build/components/block-popover/inbetween.js +24 -9
  9. package/build/components/block-popover/inbetween.js.map +1 -1
  10. package/build/components/block-popover/index.js +29 -2
  11. package/build/components/block-popover/index.js.map +1 -1
  12. package/build/components/block-preview/auto.js +23 -11
  13. package/build/components/block-preview/auto.js.map +1 -1
  14. package/build/components/block-styles/index.js +1 -3
  15. package/build/components/block-styles/index.js.map +1 -1
  16. package/build/components/block-tools/index.js +18 -13
  17. package/build/components/block-tools/index.js.map +1 -1
  18. package/build/components/block-tools/insertion-point.js +5 -3
  19. package/build/components/block-tools/insertion-point.js.map +1 -1
  20. package/build/components/block-variation-picker/index.native.js +2 -1
  21. package/build/components/block-variation-picker/index.native.js.map +1 -1
  22. package/build/components/button-block-appender/index.native.js +1 -0
  23. package/build/components/button-block-appender/index.native.js.map +1 -1
  24. package/build/components/list-view/index.js +2 -1
  25. package/build/components/list-view/index.js.map +1 -1
  26. package/build/components/rich-text/format-toolbar-container.js +8 -1
  27. package/build/components/rich-text/format-toolbar-container.js.map +1 -1
  28. package/build/components/rich-text/index.js +12 -0
  29. package/build/components/rich-text/index.js.map +1 -1
  30. package/build/components/url-input/index.js +1 -3
  31. package/build/components/url-input/index.js.map +1 -1
  32. package/build/components/use-setting/index.js +16 -12
  33. package/build/components/use-setting/index.js.map +1 -1
  34. package/build/components/writing-flow/use-arrow-nav.js +7 -1
  35. package/build/components/writing-flow/use-arrow-nav.js.map +1 -1
  36. package/build/store/reducer.js +33 -19
  37. package/build/store/reducer.js.map +1 -1
  38. package/build/store/selectors.js +4 -4
  39. package/build/store/selectors.js.map +1 -1
  40. package/build-module/components/block-list/index.js +22 -8
  41. package/build-module/components/block-list/index.js.map +1 -1
  42. package/build-module/components/block-list/use-block-props/index.js +8 -2
  43. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  44. package/build-module/components/block-list/use-in-between-inserter.js +11 -2
  45. package/build-module/components/block-list/use-in-between-inserter.js.map +1 -1
  46. package/build-module/components/block-popover/inbetween.js +24 -9
  47. package/build-module/components/block-popover/inbetween.js.map +1 -1
  48. package/build-module/components/block-popover/index.js +29 -3
  49. package/build-module/components/block-popover/index.js.map +1 -1
  50. package/build-module/components/block-preview/auto.js +22 -10
  51. package/build-module/components/block-preview/auto.js.map +1 -1
  52. package/build-module/components/block-styles/index.js +1 -2
  53. package/build-module/components/block-styles/index.js.map +1 -1
  54. package/build-module/components/block-tools/index.js +18 -12
  55. package/build-module/components/block-tools/index.js.map +1 -1
  56. package/build-module/components/block-tools/insertion-point.js +5 -3
  57. package/build-module/components/block-tools/insertion-point.js.map +1 -1
  58. package/build-module/components/block-variation-picker/index.native.js +2 -1
  59. package/build-module/components/block-variation-picker/index.native.js.map +1 -1
  60. package/build-module/components/button-block-appender/index.native.js +1 -0
  61. package/build-module/components/button-block-appender/index.native.js.map +1 -1
  62. package/build-module/components/list-view/index.js +2 -1
  63. package/build-module/components/list-view/index.js.map +1 -1
  64. package/build-module/components/rich-text/format-toolbar-container.js +6 -1
  65. package/build-module/components/rich-text/format-toolbar-container.js.map +1 -1
  66. package/build-module/components/rich-text/index.js +12 -0
  67. package/build-module/components/rich-text/index.js.map +1 -1
  68. package/build-module/components/url-input/index.js +1 -2
  69. package/build-module/components/url-input/index.js.map +1 -1
  70. package/build-module/components/use-setting/index.js +16 -12
  71. package/build-module/components/use-setting/index.js.map +1 -1
  72. package/build-module/components/writing-flow/use-arrow-nav.js +8 -2
  73. package/build-module/components/writing-flow/use-arrow-nav.js.map +1 -1
  74. package/build-module/store/reducer.js +29 -18
  75. package/build-module/store/reducer.js.map +1 -1
  76. package/build-module/store/selectors.js +4 -4
  77. package/build-module/store/selectors.js.map +1 -1
  78. package/build-style/style-rtl.css +3 -5
  79. package/build-style/style.css +3 -5
  80. package/package.json +28 -28
  81. package/src/components/block-controls/test/index.js +84 -8
  82. package/src/components/block-list/index.js +42 -6
  83. package/src/components/block-list/use-block-props/index.js +6 -1
  84. package/src/components/block-list/use-in-between-inserter.js +9 -3
  85. package/src/components/block-mover/style.scss +2 -7
  86. package/src/components/block-popover/inbetween.js +34 -10
  87. package/src/components/block-popover/index.js +47 -2
  88. package/src/components/block-preview/auto.js +77 -65
  89. package/src/components/block-styles/index.js +1 -2
  90. package/src/components/block-tools/index.js +16 -10
  91. package/src/components/block-tools/insertion-point.js +3 -3
  92. package/src/components/block-variation-picker/index.native.js +1 -0
  93. package/src/components/button-block-appender/index.native.js +1 -0
  94. package/src/components/link-control/test/index.js +1 -2
  95. package/src/components/list-view/index.js +1 -0
  96. package/src/components/rich-text/format-toolbar-container.js +8 -2
  97. package/src/components/rich-text/index.js +14 -0
  98. package/src/components/url-input/index.js +6 -2
  99. package/src/components/use-setting/index.js +18 -16
  100. package/src/components/warning/style.scss +1 -0
  101. package/src/components/writing-flow/use-arrow-nav.js +11 -1
  102. package/src/store/reducer.js +22 -14
  103. package/src/store/selectors.js +4 -4
  104. package/src/store/test/reducer.js +0 -17
  105. package/src/components/block-controls/test/__snapshots__/index.js.snap +0 -64
@@ -20,13 +20,12 @@ let MemoizedBlockList;
20
20
 
21
21
  const MAX_HEIGHT = 2000;
22
22
 
23
- function AutoBlockPreview( {
23
+ function ScaledBlockPreview( {
24
24
  viewportWidth,
25
+ containerWidth,
25
26
  __experimentalPadding,
26
27
  __experimentalMinHeight,
27
28
  } ) {
28
- const [ containerResizeListener, { width: containerWidth } ] =
29
- useResizeObserver();
30
29
  const [ contentResizeListener, { height: contentHeight } ] =
31
30
  useResizeObserver();
32
31
  const { styles, assets, duotone } = useSelect( ( select ) => {
@@ -62,71 +61,84 @@ function AutoBlockPreview( {
62
61
 
63
62
  const scale = containerWidth / viewportWidth;
64
63
  return (
65
- <div className="block-editor-block-preview__container">
66
- { containerResizeListener }
67
- <Disabled
68
- className="block-editor-block-preview__content"
64
+ <Disabled
65
+ className="block-editor-block-preview__content"
66
+ style={ {
67
+ transform: `scale(${ scale })`,
68
+ height: contentHeight * scale,
69
+ maxHeight:
70
+ contentHeight > MAX_HEIGHT ? MAX_HEIGHT * scale : undefined,
71
+ minHeight: __experimentalMinHeight,
72
+ } }
73
+ >
74
+ <Iframe
75
+ head={ <EditorStyles styles={ editorStyles } /> }
76
+ assets={ assets }
77
+ contentRef={ useRefEffect( ( bodyElement ) => {
78
+ const {
79
+ ownerDocument: { documentElement },
80
+ } = bodyElement;
81
+ documentElement.classList.add(
82
+ 'block-editor-block-preview__content-iframe'
83
+ );
84
+ documentElement.style.position = 'absolute';
85
+ documentElement.style.width = '100%';
86
+ bodyElement.style.padding = __experimentalPadding + 'px';
87
+
88
+ // Necessary for contentResizeListener to work.
89
+ bodyElement.style.boxSizing = 'border-box';
90
+ bodyElement.style.position = 'absolute';
91
+ bodyElement.style.width = '100%';
92
+ }, [] ) }
93
+ aria-hidden
94
+ tabIndex={ -1 }
69
95
  style={ {
70
- transform: `scale(${ scale })`,
71
- height: contentHeight * scale,
72
- maxHeight:
73
- contentHeight > MAX_HEIGHT
74
- ? MAX_HEIGHT * scale
75
- : undefined,
76
- minHeight: __experimentalMinHeight,
96
+ position: 'absolute',
97
+ width: viewportWidth,
98
+ height: contentHeight,
99
+ pointerEvents: 'none',
100
+ // This is a catch-all max-height for patterns.
101
+ // See: https://github.com/WordPress/gutenberg/pull/38175.
102
+ maxHeight: MAX_HEIGHT,
103
+ minHeight:
104
+ scale !== 0 && scale < 1 && __experimentalMinHeight
105
+ ? __experimentalMinHeight / scale
106
+ : __experimentalMinHeight,
77
107
  } }
78
108
  >
79
- <Iframe
80
- head={ <EditorStyles styles={ editorStyles } /> }
81
- assets={ assets }
82
- contentRef={ useRefEffect( ( bodyElement ) => {
83
- const {
84
- ownerDocument: { documentElement },
85
- } = bodyElement;
86
- documentElement.classList.add(
87
- 'block-editor-block-preview__content-iframe'
88
- );
89
- documentElement.style.position = 'absolute';
90
- documentElement.style.width = '100%';
91
- bodyElement.style.padding =
92
- __experimentalPadding + 'px';
93
-
94
- // Necessary for contentResizeListener to work.
95
- bodyElement.style.boxSizing = 'border-box';
96
- bodyElement.style.position = 'absolute';
97
- bodyElement.style.width = '100%';
98
- }, [] ) }
99
- aria-hidden
100
- tabIndex={ -1 }
101
- style={ {
102
- position: 'absolute',
103
- width: viewportWidth,
104
- height: contentHeight,
105
- pointerEvents: 'none',
106
- // This is a catch-all max-height for patterns.
107
- // See: https://github.com/WordPress/gutenberg/pull/38175.
108
- maxHeight: MAX_HEIGHT,
109
- minHeight:
110
- scale !== 0 && scale < 1 && __experimentalMinHeight
111
- ? __experimentalMinHeight / scale
112
- : __experimentalMinHeight,
113
- } }
114
- >
115
- { contentResizeListener }
116
- {
117
- /* Filters need to be rendered before children to avoid Safari rendering issues. */
118
- svgFilters.map( ( preset ) => (
119
- <PresetDuotoneFilter
120
- preset={ preset }
121
- key={ preset.slug }
122
- />
123
- ) )
124
- }
125
- <MemoizedBlockList renderAppender={ false } />
126
- </Iframe>
127
- </Disabled>
128
- </div>
109
+ { contentResizeListener }
110
+ {
111
+ /* Filters need to be rendered before children to avoid Safari rendering issues. */
112
+ svgFilters.map( ( preset ) => (
113
+ <PresetDuotoneFilter
114
+ preset={ preset }
115
+ key={ preset.slug }
116
+ />
117
+ ) )
118
+ }
119
+ <MemoizedBlockList renderAppender={ false } />
120
+ </Iframe>
121
+ </Disabled>
129
122
  );
130
123
  }
131
124
 
132
- export default AutoBlockPreview;
125
+ export default function AutoBlockPreview( props ) {
126
+ const [ containerResizeListener, { width: containerWidth } ] =
127
+ useResizeObserver();
128
+
129
+ return (
130
+ <>
131
+ <div style={ { position: 'relative', width: '100%', height: 0 } }>
132
+ { containerResizeListener }
133
+ </div>
134
+ <div className="block-editor-block-preview__container">
135
+ { !! containerWidth && (
136
+ <ScaledBlockPreview
137
+ { ...props }
138
+ containerWidth={ containerWidth }
139
+ />
140
+ ) }
141
+ </div>
142
+ </>
143
+ );
144
+ }
@@ -1,14 +1,13 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { debounce } from 'lodash';
5
4
  import classnames from 'classnames';
6
5
 
7
6
  /**
8
7
  * WordPress dependencies
9
8
  */
10
9
  import { useState, useLayoutEffect } from '@wordpress/element';
11
- import { useViewportMatch } from '@wordpress/compose';
10
+ import { debounce, useViewportMatch } from '@wordpress/compose';
12
11
  import {
13
12
  Button,
14
13
  __experimentalTruncate as Truncate,
@@ -20,6 +20,17 @@ import BlockContextualToolbar from './block-contextual-toolbar';
20
20
  import usePopoverScroll from '../block-popover/use-popover-scroll';
21
21
  import ZoomOutModeInserters from './zoom-out-mode-inserters';
22
22
 
23
+ function selector( select ) {
24
+ const { __unstableGetEditorMode, getSettings, isTyping } =
25
+ select( blockEditorStore );
26
+
27
+ return {
28
+ isZoomOutMode: __unstableGetEditorMode() === 'zoom-out',
29
+ hasFixedToolbar: getSettings().hasFixedToolbar,
30
+ isTyping: isTyping(),
31
+ };
32
+ }
33
+
23
34
  /**
24
35
  * Renders block tools (the block toolbar, select/navigation mode toolbar, the
25
36
  * insertion point and a slot for the inline rich text toolbar). Must be wrapped
@@ -35,15 +46,10 @@ export default function BlockTools( {
35
46
  ...props
36
47
  } ) {
37
48
  const isLargeViewport = useViewportMatch( 'medium' );
38
- const { hasFixedToolbar, isZoomOutMode } = useSelect( ( select ) => {
39
- const { __unstableGetEditorMode, getSettings } =
40
- select( blockEditorStore );
41
-
42
- return {
43
- isZoomOutMode: __unstableGetEditorMode() === 'zoom-out',
44
- hasFixedToolbar: getSettings().hasFixedToolbar,
45
- };
46
- }, [] );
49
+ const { hasFixedToolbar, isZoomOutMode, isTyping } = useSelect(
50
+ selector,
51
+ []
52
+ );
47
53
  const isMatch = useShortcutEventMatch();
48
54
  const { getSelectedBlockClientIds, getBlockRootClientId } =
49
55
  useSelect( blockEditorStore );
@@ -118,7 +124,7 @@ export default function BlockTools( {
118
124
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
119
125
  <div { ...props } onKeyDown={ onKeyDown }>
120
126
  <InsertionPointOpenRef.Provider value={ useRef( false ) }>
121
- { ! isZoomOutMode && (
127
+ { ! isTyping && ! isZoomOutMode && (
122
128
  <InsertionPoint
123
129
  __unstableContentRef={ __unstableContentRef }
124
130
  />
@@ -149,13 +149,13 @@ function InsertionPointPopover( {
149
149
  ...( ! isVertical ? horizontalLine.rest : verticalLine.rest ),
150
150
  opacity: 1,
151
151
  borderRadius: '2px',
152
- transition: { delay: isInserterShown ? 0.4 : 0 },
152
+ transition: { delay: isInserterShown ? 0.1 : 0, type: 'tween' },
153
153
  },
154
154
  hover: {
155
155
  ...( ! isVertical ? horizontalLine.hover : verticalLine.hover ),
156
156
  opacity: 1,
157
157
  borderRadius: '2px',
158
- transition: { delay: 0.4 },
158
+ transition: { delay: 0.1, type: 'tween' },
159
159
  },
160
160
  };
161
161
 
@@ -165,7 +165,7 @@ function InsertionPointPopover( {
165
165
  },
166
166
  rest: {
167
167
  scale: 1,
168
- transition: { delay: 0.2 },
168
+ transition: { type: 'tween' },
169
169
  },
170
170
  };
171
171
 
@@ -85,6 +85,7 @@ function BlockVariationPicker( { isVisible, onClose, clientId, variations } ) {
85
85
  title={ __( 'Select a layout' ) }
86
86
  contentStyle={ styles.contentStyle }
87
87
  leftButton={ leftButton }
88
+ testID="block-variation-modal"
88
89
  >
89
90
  <ScrollView
90
91
  horizontal
@@ -43,6 +43,7 @@ function ButtonBlockAppender( {
43
43
  rootClientId={ rootClientId }
44
44
  renderToggle={ ( { onToggle, disabled, isOpen } ) => (
45
45
  <Button
46
+ testID="appender-button"
46
47
  onClick={ onAddBlock || onToggle }
47
48
  aria-expanded={ isOpen }
48
49
  disabled={ disabled }
@@ -24,8 +24,7 @@ import {
24
24
  } from './fixtures';
25
25
 
26
26
  // Mock debounce() so that it runs instantly.
27
- jest.mock( 'lodash', () => ( {
28
- ...jest.requireActual( 'lodash' ),
27
+ jest.mock( '@wordpress/compose/src/utils/debounce', () => ( {
29
28
  debounce: ( fn ) => {
30
29
  fn.cancel = jest.fn();
31
30
  return fn;
@@ -188,6 +188,7 @@ function ListView(
188
188
  onCollapseRow={ collapseRow }
189
189
  onExpandRow={ expandRow }
190
190
  onFocusRow={ focusRow }
191
+ applicationAriaLabel={ __( 'Block navigation structure' ) }
191
192
  >
192
193
  <ListViewContext.Provider value={ contextValue }>
193
194
  <ListViewBranch
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
+ import { __ } from '@wordpress/i18n';
4
5
  import { Popover, ToolbarGroup } from '@wordpress/components';
5
6
  import { useSelect } from '@wordpress/data';
6
7
  import {
@@ -15,6 +16,7 @@ import {
15
16
  */
16
17
  import BlockControls from '../block-controls';
17
18
  import FormatToolbar from './format-toolbar';
19
+ import NavigableToolbar from '../navigable-toolbar';
18
20
  import { store as blockEditorStore } from '../../store';
19
21
 
20
22
  function InlineSelectionToolbar( {
@@ -46,11 +48,15 @@ function InlineToolbar( { popoverAnchor } ) {
46
48
  className="block-editor-rich-text__inline-format-toolbar"
47
49
  __unstableSlotName="block-toolbar"
48
50
  >
49
- <div className="block-editor-rich-text__inline-format-toolbar-group">
51
+ <NavigableToolbar
52
+ className="block-editor-rich-text__inline-format-toolbar-group"
53
+ /* translators: accessibility text for the inline format toolbar */
54
+ aria-label={ __( 'Format tools' ) }
55
+ >
50
56
  <ToolbarGroup>
51
57
  <FormatToolbar />
52
58
  </ToolbarGroup>
53
- </div>
59
+ </NavigableToolbar>
54
60
  </Popover>
55
61
  );
56
62
  }
@@ -173,6 +173,13 @@ function RichTextWrapper(
173
173
 
174
174
  // Handle deprecated format.
175
175
  if ( Array.isArray( originalValue ) ) {
176
+ deprecated( 'wp.blockEditor.RichText value prop as children type', {
177
+ since: '6.1',
178
+ version: '6.3',
179
+ alternative: 'value prop as string',
180
+ link: 'https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/introducing-attributes-and-editable-fields/',
181
+ } );
182
+
176
183
  adjustedValue = childrenSource.toHTML( originalValue );
177
184
  adjustedOnChange = ( newValue ) =>
178
185
  originalOnChange(
@@ -436,6 +443,13 @@ ForwardedRichTextContainer.Content = ( {
436
443
  } ) => {
437
444
  // Handle deprecated `children` and `node` sources.
438
445
  if ( Array.isArray( value ) ) {
446
+ deprecated( 'wp.blockEditor.RichText value prop as children type', {
447
+ since: '6.1',
448
+ version: '6.3',
449
+ alternative: 'value prop as string',
450
+ link: 'https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/introducing-attributes-and-editable-fields/',
451
+ } );
452
+
439
453
  value = childrenSource.toHTML( value );
440
454
  }
441
455
 
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { debounce } from 'lodash';
5
4
  import classnames from 'classnames';
6
5
  import scrollIntoView from 'dom-scroll-into-view';
7
6
 
@@ -18,7 +17,12 @@ import {
18
17
  withSpokenMessages,
19
18
  Popover,
20
19
  } from '@wordpress/components';
21
- import { withInstanceId, withSafeTimeout, compose } from '@wordpress/compose';
20
+ import {
21
+ compose,
22
+ debounce,
23
+ withInstanceId,
24
+ withSafeTimeout,
25
+ } from '@wordpress/compose';
22
26
  import { withSelect } from '@wordpress/data';
23
27
  import { isURL } from '@wordpress/url';
24
28
 
@@ -21,10 +21,8 @@ import { store as blockEditorStore } from '../../store';
21
21
  const blockedPaths = [ 'color', 'border', 'typography', 'spacing' ];
22
22
 
23
23
  const deprecatedFlags = {
24
- 'color.palette': ( settings ) =>
25
- settings.colors === undefined ? undefined : settings.colors,
26
- 'color.gradients': ( settings ) =>
27
- settings.gradients === undefined ? undefined : settings.gradients,
24
+ 'color.palette': ( settings ) => settings.colors,
25
+ 'color.gradients': ( settings ) => settings.gradients,
28
26
  'color.custom': ( settings ) =>
29
27
  settings.disableCustomColors === undefined
30
28
  ? undefined
@@ -33,8 +31,7 @@ const deprecatedFlags = {
33
31
  settings.disableCustomGradients === undefined
34
32
  ? undefined
35
33
  : ! settings.disableCustomGradients,
36
- 'typography.fontSizes': ( settings ) =>
37
- settings.fontSizes === undefined ? undefined : settings.fontSizes,
34
+ 'typography.fontSizes': ( settings ) => settings.fontSizes,
38
35
  'typography.customFontSize': ( settings ) =>
39
36
  settings.disableCustomFontSizes === undefined
40
37
  ? undefined
@@ -109,7 +106,7 @@ const removeCustomPrefixes = ( path ) => {
109
106
  export default function useSetting( path ) {
110
107
  const { name: blockName, clientId } = useBlockEditContext();
111
108
 
112
- const setting = useSelect(
109
+ return useSelect(
113
110
  ( select ) => {
114
111
  if ( blockedPaths.includes( path ) ) {
115
112
  // eslint-disable-next-line no-console
@@ -120,14 +117,20 @@ export default function useSetting( path ) {
120
117
  }
121
118
 
122
119
  let result;
120
+
123
121
  const normalizedPath = removeCustomPrefixes( path );
124
122
 
125
123
  // 1. Take settings from the block instance or its ancestors.
124
+ // Start from the current block and work our way up the ancestors.
126
125
  const candidates = [
127
- ...select( blockEditorStore ).getBlockParents( clientId ),
128
- clientId, // The current block is added last, so it overwrites any ancestor.
126
+ clientId,
127
+ ...select( blockEditorStore ).getBlockParents(
128
+ clientId,
129
+ /* ascending */ true
130
+ ),
129
131
  ];
130
- candidates.forEach( ( candidateClientId ) => {
132
+
133
+ for ( const candidateClientId of candidates ) {
131
134
  const candidateBlockName =
132
135
  select( blockEditorStore ).getBlockName(
133
136
  candidateClientId
@@ -143,17 +146,18 @@ export default function useSetting( path ) {
143
146
  select( blockEditorStore ).getBlockAttributes(
144
147
  candidateClientId
145
148
  );
146
- const candidateResult =
149
+ result =
147
150
  get(
148
151
  candidateAtts,
149
152
  `settings.blocks.${ blockName }.${ normalizedPath }`
150
153
  ) ??
151
154
  get( candidateAtts, `settings.${ normalizedPath }` );
152
- if ( candidateResult !== undefined ) {
153
- result = candidateResult;
155
+ if ( result !== undefined ) {
156
+ // Stop the search for more distant ancestors and move on.
157
+ break;
154
158
  }
155
159
  }
156
- } );
160
+ }
157
161
 
158
162
  // 2. Fall back to the settings from the block editor store (__experimentalFeatures).
159
163
  const settings = select( blockEditorStore ).getSettings();
@@ -188,6 +192,4 @@ export default function useSetting( path ) {
188
192
  },
189
193
  [ blockName, clientId, path ]
190
194
  );
191
-
192
- return setting;
193
195
  }
@@ -33,6 +33,7 @@
33
33
  }
34
34
 
35
35
  .block-editor-warning__actions {
36
+ align-items: center;
36
37
  display: flex;
37
38
  margin-top: 1em;
38
39
  }
@@ -17,7 +17,7 @@ import { useRefEffect } from '@wordpress/compose';
17
17
  /**
18
18
  * Internal dependencies
19
19
  */
20
- import { getBlockClientId } from '../../utils/dom';
20
+ import { getBlockClientId, isInSameBlock } from '../../utils/dom';
21
21
  import { store as blockEditorStore } from '../../store';
22
22
 
23
23
  /**
@@ -101,6 +101,16 @@ export function getClosestTabbable(
101
101
  }
102
102
 
103
103
  function isTabCandidate( node ) {
104
+ // Skip if there's only one child that is content editable (and thus a
105
+ // better candidate).
106
+ if (
107
+ node.children.length === 1 &&
108
+ isInSameBlock( node, node.firstElementChild ) &&
109
+ node.firstElementChild.getAttribute( 'contenteditable' ) === 'true'
110
+ ) {
111
+ return;
112
+ }
113
+
104
114
  // Not a candidate if the node is not tabbable.
105
115
  if ( ! focus.tabbable.isTabbableIndex( node ) ) {
106
116
  return false;
@@ -614,7 +614,6 @@ const withBlockReset = ( reducer ) => ( state, action ) => {
614
614
  order: mapBlockOrder( action.blocks ),
615
615
  parents: mapBlockParents( action.blocks ),
616
616
  controlledInnerBlocks: {},
617
- visibility: {},
618
617
  };
619
618
 
620
619
  const subTree = buildBlockTree( newState, action.blocks );
@@ -1162,17 +1161,6 @@ export const blocks = flow(
1162
1161
  }
1163
1162
  return state;
1164
1163
  },
1165
-
1166
- visibility( state = {}, action ) {
1167
- if ( action.type === 'SET_BLOCK_VISIBILITY' ) {
1168
- return {
1169
- ...state,
1170
- ...action.updates,
1171
- };
1172
- }
1173
-
1174
- return state;
1175
- },
1176
1164
  } );
1177
1165
 
1178
1166
  /**
@@ -1215,6 +1203,25 @@ export function draggedBlocks( state = [], action ) {
1215
1203
  return state;
1216
1204
  }
1217
1205
 
1206
+ /**
1207
+ * Reducer tracking the visible blocks.
1208
+ *
1209
+ * @param {Record<string,boolean>} state Current state.
1210
+ * @param {Object} action Dispatched action.
1211
+ *
1212
+ * @return {Record<string,boolean>} Block visibility.
1213
+ */
1214
+ export function blockVisibility( state = {}, action ) {
1215
+ if ( action.type === 'SET_BLOCK_VISIBILITY' ) {
1216
+ return {
1217
+ ...state,
1218
+ ...action.updates,
1219
+ };
1220
+ }
1221
+
1222
+ return state;
1223
+ }
1224
+
1218
1225
  /**
1219
1226
  * Internal helper reducer for selectionStart and selectionEnd. Can hold a block
1220
1227
  * selection, represented by an object with property clientId.
@@ -1660,7 +1667,7 @@ export function hasBlockMovingClientId( state = null, action ) {
1660
1667
  *
1661
1668
  * @return {[string,Object]} Updated state.
1662
1669
  */
1663
- export function lastBlockAttributesChange( state, action ) {
1670
+ export function lastBlockAttributesChange( state = null, action ) {
1664
1671
  switch ( action.type ) {
1665
1672
  case 'UPDATE_BLOCK':
1666
1673
  if ( ! action.updates.attributes ) {
@@ -1681,7 +1688,7 @@ export function lastBlockAttributesChange( state, action ) {
1681
1688
  );
1682
1689
  }
1683
1690
 
1684
- return null;
1691
+ return state;
1685
1692
  }
1686
1693
 
1687
1694
  /**
@@ -1813,4 +1820,5 @@ export default combineReducers( {
1813
1820
  highlightedBlock,
1814
1821
  lastBlockInserted,
1815
1822
  temporarilyEditingAsBlocks,
1823
+ blockVisibility,
1816
1824
  } );
@@ -2670,7 +2670,7 @@ export function wasBlockJustInserted( state, clientId, source ) {
2670
2670
  * @return {boolean} True if the block is visible.
2671
2671
  */
2672
2672
  export function isBlockVisible( state, clientId ) {
2673
- return state.blocks.visibility?.[ clientId ] ?? true;
2673
+ return state.blockVisibility?.[ clientId ] ?? true;
2674
2674
  }
2675
2675
 
2676
2676
  /**
@@ -2682,12 +2682,12 @@ export function isBlockVisible( state, clientId ) {
2682
2682
  export const __unstableGetVisibleBlocks = createSelector(
2683
2683
  ( state ) => {
2684
2684
  return new Set(
2685
- Object.keys( state.blocks.visibility ).filter(
2686
- ( key ) => state.blocks.visibility[ key ]
2685
+ Object.keys( state.blockVisibility ).filter(
2686
+ ( key ) => state.blockVisibility[ key ]
2687
2687
  )
2688
2688
  );
2689
2689
  },
2690
- ( state ) => [ state.blocks.visibility ]
2690
+ ( state ) => [ state.blockVisibility ]
2691
2691
  );
2692
2692
 
2693
2693
  export const __unstableGetContentLockingParent = createSelector(
@@ -292,7 +292,6 @@ describe( 'state', () => {
292
292
  chicken: '',
293
293
  },
294
294
  controlledInnerBlocks: {},
295
- visibility: {},
296
295
  } );
297
296
  expect( state.tree.chicken ).not.toBe(
298
297
  existingState.tree.chicken
@@ -375,7 +374,6 @@ describe( 'state', () => {
375
374
  chicken: '',
376
375
  },
377
376
  controlledInnerBlocks: {},
378
- visibility: {},
379
377
  } );
380
378
  expect( state.tree.chicken ).not.toBe(
381
379
  existingState.tree.chicken
@@ -525,7 +523,6 @@ describe( 'state', () => {
525
523
  [ newChildBlockId3 ]: 'chicken',
526
524
  },
527
525
  controlledInnerBlocks: {},
528
- visibility: {},
529
526
  } );
530
527
 
531
528
  expect( state.tree[ '' ].innerBlocks[ 0 ] ).toBe(
@@ -635,7 +632,6 @@ describe( 'state', () => {
635
632
  [ newChildBlockId ]: 'chicken',
636
633
  },
637
634
  controlledInnerBlocks: {},
638
- visibility: {},
639
635
  } );
640
636
 
641
637
  // The block object of the parent should be updated.
@@ -657,7 +653,6 @@ describe( 'state', () => {
657
653
  isIgnoredChange: false,
658
654
  tree: {},
659
655
  controlledInnerBlocks: {},
660
- visibility: {},
661
656
  } );
662
657
  } );
663
658
 
@@ -3085,18 +3080,6 @@ describe( 'state', () => {
3085
3080
  'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': { food: 'banana' },
3086
3081
  } );
3087
3082
  } );
3088
-
3089
- it( 'returns null on anything other than block attributes update', () => {
3090
- const original = deepFreeze( {
3091
- 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': { food: 'banana' },
3092
- } );
3093
-
3094
- const state = lastBlockAttributesChange( original, {
3095
- type: '__INERT__',
3096
- } );
3097
-
3098
- expect( state ).toBe( null );
3099
- } );
3100
3083
  } );
3101
3084
 
3102
3085
  describe( 'lastBlockInserted', () => {