@wordpress/editor 13.29.0 → 13.31.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.
- package/CHANGELOG.md +4 -0
- package/build/bindings/index.js +5 -3
- package/build/bindings/index.js.map +1 -1
- package/build/bindings/post-meta.js +2 -1
- package/build/bindings/post-meta.js.map +1 -1
- package/build/components/block-removal-warnings/index.js +70 -0
- package/build/components/block-removal-warnings/index.js.map +1 -0
- package/build/components/commands/index.js +22 -14
- package/build/components/commands/index.js.map +1 -1
- package/build/components/document-bar/index.js +2 -2
- package/build/components/document-bar/index.js.map +1 -1
- package/build/components/document-outline/check.js +8 -7
- package/build/components/document-outline/check.js.map +1 -1
- package/build/components/document-outline/index.js +27 -28
- package/build/components/document-outline/index.js.map +1 -1
- package/build/components/document-tools/index.js +5 -3
- package/build/components/document-tools/index.js.map +1 -1
- package/build/components/editor-help/intro-to-blocks.native.js.map +1 -1
- package/build/components/entities-saved-states/entity-type-list.js.map +1 -1
- package/build/components/entities-saved-states/hooks/use-is-dirty.js +10 -16
- package/build/components/entities-saved-states/hooks/use-is-dirty.js.map +1 -1
- package/build/components/entities-saved-states/index.js +17 -3
- package/build/components/entities-saved-states/index.js.map +1 -1
- package/build/components/error-boundary/index.native.js +133 -0
- package/build/components/error-boundary/index.native.js.map +1 -0
- package/build/components/index.js +9 -8
- package/build/components/index.js.map +1 -1
- package/build/components/index.native.js +9 -1
- package/build/components/index.native.js.map +1 -1
- package/build/components/list-view-sidebar/index.js +1 -1
- package/build/components/list-view-sidebar/index.js.map +1 -1
- package/build/components/plugin-document-setting-panel/index.js +123 -0
- package/build/components/plugin-document-setting-panel/index.js.map +1 -0
- package/build/components/post-featured-image/index.js +3 -8
- package/build/components/post-featured-image/index.js.map +1 -1
- package/build/components/post-featured-image/panel.js +7 -3
- package/build/components/post-featured-image/panel.js.map +1 -1
- package/build/components/post-locked-modal/index.js.map +1 -1
- package/build/components/post-preview-button/index.js +2 -1
- package/build/components/post-preview-button/index.js.map +1 -1
- package/build/components/post-publish-button/index.js +2 -1
- package/build/components/post-publish-button/index.js.map +1 -1
- package/build/components/post-publish-panel/index.js.map +1 -1
- package/build/components/post-publish-panel/maybe-upload-media.js.map +1 -1
- package/build/components/post-sync-status/index.js +0 -72
- package/build/components/post-sync-status/index.js.map +1 -1
- package/build/components/post-taxonomies/flat-term-selector.js +7 -3
- package/build/components/post-taxonomies/flat-term-selector.js.map +1 -1
- package/build/components/post-taxonomies/hierarchical-term-selector.js +3 -0
- package/build/components/post-taxonomies/hierarchical-term-selector.js.map +1 -1
- package/build/components/post-title/index.native.js.map +1 -1
- package/build/components/post-title/post-title-raw.js.map +1 -1
- package/build/components/post-view-link/index.js +2 -1
- package/build/components/post-view-link/index.js.map +1 -1
- package/build/components/provider/disable-non-page-content-blocks.js +23 -30
- package/build/components/provider/disable-non-page-content-blocks.js.map +1 -1
- package/build/components/provider/index.js +3 -2
- package/build/components/provider/index.js.map +1 -1
- package/build/components/provider/use-hide-blocks-from-inserter.js +4 -3
- package/build/components/provider/use-hide-blocks-from-inserter.js.map +1 -1
- package/build/hooks/index.js +1 -1
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/{pattern-partial-syncing.js → pattern-overrides.js} +13 -9
- package/build/hooks/pattern-overrides.js.map +1 -0
- package/build/private-apis.js +0 -2
- package/build/private-apis.js.map +1 -1
- package/build-module/bindings/index.js +5 -3
- package/build-module/bindings/index.js.map +1 -1
- package/build-module/bindings/post-meta.js +2 -1
- package/build-module/bindings/post-meta.js.map +1 -1
- package/build-module/components/block-removal-warnings/index.js +64 -0
- package/build-module/components/block-removal-warnings/index.js.map +1 -0
- package/build-module/components/commands/index.js +22 -14
- package/build-module/components/commands/index.js.map +1 -1
- package/build-module/components/document-bar/index.js +2 -2
- package/build-module/components/document-bar/index.js.map +1 -1
- package/build-module/components/document-outline/check.js +9 -8
- package/build-module/components/document-outline/check.js.map +1 -1
- package/build-module/components/document-outline/index.js +27 -27
- package/build-module/components/document-outline/index.js.map +1 -1
- package/build-module/components/document-tools/index.js +5 -3
- package/build-module/components/document-tools/index.js.map +1 -1
- package/build-module/components/editor-help/intro-to-blocks.native.js.map +1 -1
- package/build-module/components/entities-saved-states/entity-type-list.js.map +1 -1
- package/build-module/components/entities-saved-states/hooks/use-is-dirty.js +10 -16
- package/build-module/components/entities-saved-states/hooks/use-is-dirty.js.map +1 -1
- package/build-module/components/entities-saved-states/index.js +18 -4
- package/build-module/components/entities-saved-states/index.js.map +1 -1
- package/build-module/components/error-boundary/index.native.js +125 -0
- package/build-module/components/error-boundary/index.native.js.map +1 -0
- package/build-module/components/index.js +2 -1
- package/build-module/components/index.js.map +1 -1
- package/build-module/components/index.native.js +1 -0
- package/build-module/components/index.native.js.map +1 -1
- package/build-module/components/list-view-sidebar/index.js +1 -1
- package/build-module/components/list-view-sidebar/index.js.map +1 -1
- package/build-module/components/plugin-document-setting-panel/index.js +115 -0
- package/build-module/components/plugin-document-setting-panel/index.js.map +1 -0
- package/build-module/components/post-featured-image/index.js +4 -9
- package/build-module/components/post-featured-image/index.js.map +1 -1
- package/build-module/components/post-featured-image/panel.js +6 -2
- package/build-module/components/post-featured-image/panel.js.map +1 -1
- package/build-module/components/post-locked-modal/index.js.map +1 -1
- package/build-module/components/post-preview-button/index.js +2 -1
- package/build-module/components/post-preview-button/index.js.map +1 -1
- package/build-module/components/post-publish-button/index.js +2 -1
- package/build-module/components/post-publish-button/index.js.map +1 -1
- package/build-module/components/post-publish-panel/index.js.map +1 -1
- package/build-module/components/post-publish-panel/maybe-upload-media.js.map +1 -1
- package/build-module/components/post-sync-status/index.js +2 -73
- package/build-module/components/post-sync-status/index.js.map +1 -1
- package/build-module/components/post-taxonomies/flat-term-selector.js +7 -3
- package/build-module/components/post-taxonomies/flat-term-selector.js.map +1 -1
- package/build-module/components/post-taxonomies/hierarchical-term-selector.js +3 -0
- package/build-module/components/post-taxonomies/hierarchical-term-selector.js.map +1 -1
- package/build-module/components/post-title/index.native.js.map +1 -1
- package/build-module/components/post-title/post-title-raw.js.map +1 -1
- package/build-module/components/post-view-link/index.js +2 -1
- package/build-module/components/post-view-link/index.js.map +1 -1
- package/build-module/components/provider/disable-non-page-content-blocks.js +24 -31
- package/build-module/components/provider/disable-non-page-content-blocks.js.map +1 -1
- package/build-module/components/provider/index.js +3 -2
- package/build-module/components/provider/index.js.map +1 -1
- package/build-module/components/provider/use-hide-blocks-from-inserter.js +4 -3
- package/build-module/components/provider/use-hide-blocks-from-inserter.js.map +1 -1
- package/build-module/hooks/index.js +1 -1
- package/build-module/hooks/index.js.map +1 -1
- package/build-module/hooks/{pattern-partial-syncing.js → pattern-overrides.js} +13 -9
- package/build-module/hooks/pattern-overrides.js.map +1 -0
- package/build-module/private-apis.js +0 -2
- package/build-module/private-apis.js.map +1 -1
- package/build-style/style-rtl.css +9 -8
- package/build-style/style.css +9 -8
- package/package.json +34 -32
- package/src/bindings/index.js +6 -3
- package/src/bindings/post-meta.js +4 -1
- package/src/components/block-removal-warnings/index.js +92 -0
- package/src/components/commands/index.js +21 -13
- package/src/components/document-bar/index.js +3 -2
- package/src/components/document-outline/check.js +8 -10
- package/src/components/document-outline/index.js +20 -24
- package/src/components/document-outline/test/index.js +26 -7
- package/src/components/document-tools/index.js +3 -3
- package/src/components/editor-help/intro-to-blocks.native.js +1 -1
- package/src/components/entities-saved-states/entity-type-list.js +1 -1
- package/src/components/entities-saved-states/hooks/use-is-dirty.js +18 -22
- package/src/components/entities-saved-states/index.js +33 -8
- package/src/components/entities-saved-states/test/use-is-dirty.js +3 -0
- package/src/components/error-boundary/index.native.js +192 -0
- package/src/components/error-boundary/style.native.scss +116 -0
- package/src/components/index.js +2 -4
- package/src/components/index.native.js +1 -0
- package/src/components/list-view-sidebar/index.js +1 -1
- package/src/components/plugin-document-setting-panel/index.js +121 -0
- package/src/components/post-featured-image/index.js +6 -15
- package/src/components/post-featured-image/panel.js +9 -3
- package/src/components/post-featured-image/style.scss +8 -13
- package/src/components/post-locked-modal/index.js +1 -1
- package/src/components/post-preview-button/index.js +1 -0
- package/src/components/post-publish-button/index.js +1 -0
- package/src/components/post-publish-panel/index.js +1 -1
- package/src/components/post-publish-panel/maybe-upload-media.js +1 -1
- package/src/components/post-publish-panel/test/__snapshots__/index.js.snap +3 -3
- package/src/components/post-sync-status/index.js +1 -94
- package/src/components/post-taxonomies/flat-term-selector.js +13 -8
- package/src/components/post-taxonomies/hierarchical-term-selector.js +3 -0
- package/src/components/post-title/index.native.js +2 -2
- package/src/components/post-title/post-title-raw.js +1 -1
- package/src/components/post-view-link/index.js +1 -0
- package/src/components/provider/disable-non-page-content-blocks.js +34 -36
- package/src/components/provider/index.js +3 -1
- package/src/components/provider/test/disable-non-page-content-blocks.js +90 -0
- package/src/components/provider/use-hide-blocks-from-inserter.js +5 -3
- package/src/hooks/index.js +1 -1
- package/src/hooks/{pattern-partial-syncing.js → pattern-overrides.js} +16 -10
- package/src/private-apis.js +0 -2
- package/build/components/provider/constants.js +0 -8
- package/build/components/provider/constants.js.map +0 -1
- package/build/hooks/pattern-partial-syncing.js.map +0 -1
- package/build-module/components/provider/constants.js +0 -2
- package/build-module/components/provider/constants.js.map +0 -1
- package/build-module/hooks/pattern-partial-syncing.js.map +0 -1
- package/src/components/provider/constants.js +0 -5
|
@@ -25,9 +25,12 @@ function useEditorCommandLoader() {
|
|
|
25
25
|
isFocusMode,
|
|
26
26
|
isPreviewMode,
|
|
27
27
|
isViewable,
|
|
28
|
+
isCodeEditingEnabled,
|
|
29
|
+
isRichEditingEnabled,
|
|
28
30
|
} = useSelect( ( select ) => {
|
|
29
31
|
const { get } = select( preferencesStore );
|
|
30
|
-
const { isListViewOpened, getCurrentPostType } =
|
|
32
|
+
const { isListViewOpened, getCurrentPostType, getEditorSettings } =
|
|
33
|
+
select( editorStore );
|
|
31
34
|
const { getSettings } = select( blockEditorStore );
|
|
32
35
|
const { getPostType } = select( coreStore );
|
|
33
36
|
|
|
@@ -40,6 +43,8 @@ function useEditorCommandLoader() {
|
|
|
40
43
|
isTopToolbar: get( 'core', 'fixedToolbar' ),
|
|
41
44
|
isPreviewMode: getSettings().__unstableIsPreviewMode,
|
|
42
45
|
isViewable: getPostType( getCurrentPostType() )?.viewable ?? false,
|
|
46
|
+
isCodeEditingEnabled: getEditorSettings().codeEditingEnabled,
|
|
47
|
+
isRichEditingEnabled: getEditorSettings().richEditingEnabled,
|
|
43
48
|
};
|
|
44
49
|
}, [] );
|
|
45
50
|
const { toggle } = useDispatch( preferencesStore );
|
|
@@ -51,6 +56,7 @@ function useEditorCommandLoader() {
|
|
|
51
56
|
toggleDistractionFree,
|
|
52
57
|
} = useDispatch( editorStore );
|
|
53
58
|
const { getCurrentPostId } = useSelect( editorStore );
|
|
59
|
+
const allowSwitchEditorMode = isCodeEditingEnabled && isRichEditingEnabled;
|
|
54
60
|
|
|
55
61
|
if ( isPreviewMode ) {
|
|
56
62
|
return { commands: [], isLoading: false };
|
|
@@ -141,18 +147,20 @@ function useEditorCommandLoader() {
|
|
|
141
147
|
},
|
|
142
148
|
} );
|
|
143
149
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
if ( allowSwitchEditorMode ) {
|
|
151
|
+
commands.push( {
|
|
152
|
+
name: 'core/toggle-code-editor',
|
|
153
|
+
label:
|
|
154
|
+
editorMode === 'visual'
|
|
155
|
+
? __( 'Open code editor' )
|
|
156
|
+
: __( 'Exit code editor' ),
|
|
157
|
+
icon: code,
|
|
158
|
+
callback: ( { close } ) => {
|
|
159
|
+
switchEditorMode( editorMode === 'visual' ? 'text' : 'visual' );
|
|
160
|
+
close();
|
|
161
|
+
},
|
|
162
|
+
} );
|
|
163
|
+
}
|
|
156
164
|
|
|
157
165
|
commands.push( {
|
|
158
166
|
name: 'core/toggle-breadcrumbs',
|
|
@@ -74,7 +74,8 @@ export default function DocumentBar() {
|
|
|
74
74
|
getEditorSettings,
|
|
75
75
|
__experimentalGetTemplateInfo: getTemplateInfo,
|
|
76
76
|
} = select( editorStore );
|
|
77
|
-
const { getEditedEntityRecord,
|
|
77
|
+
const { getEditedEntityRecord, isResolving: isResolvingSelector } =
|
|
78
|
+
select( coreStore );
|
|
78
79
|
const _postType = getCurrentPostType();
|
|
79
80
|
const _postId = getCurrentPostId();
|
|
80
81
|
const _document = getEditedEntityRecord(
|
|
@@ -86,7 +87,7 @@ export default function DocumentBar() {
|
|
|
86
87
|
return {
|
|
87
88
|
postType: _postType,
|
|
88
89
|
document: _document,
|
|
89
|
-
isResolving:
|
|
90
|
+
isResolving: isResolvingSelector(
|
|
90
91
|
'getEditedEntityRecord',
|
|
91
92
|
'postType',
|
|
92
93
|
_postType,
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { useSelect } from '@wordpress/data';
|
|
5
5
|
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
6
6
|
|
|
7
|
-
function DocumentOutlineCheck( {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
);
|
|
7
|
+
export default function DocumentOutlineCheck( { children } ) {
|
|
8
|
+
const hasHeadings = useSelect( ( select ) => {
|
|
9
|
+
const { getGlobalBlockCount } = select( blockEditorStore );
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
return getGlobalBlockCount( 'core/heading' ) > 0;
|
|
12
|
+
} );
|
|
13
|
+
|
|
14
|
+
if ( hasHeadings ) {
|
|
13
15
|
return null;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
return children;
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
export default withSelect( ( select ) => ( {
|
|
20
|
-
blocks: select( blockEditorStore ).getBlocks(),
|
|
21
|
-
} ) )( DocumentOutlineCheck );
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
|
-
import {
|
|
6
|
-
import { withSelect, useDispatch } from '@wordpress/data';
|
|
5
|
+
import { useDispatch, useSelect } from '@wordpress/data';
|
|
7
6
|
import { create, getTextContent } from '@wordpress/rich-text';
|
|
8
7
|
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
9
8
|
import { store as coreStore } from '@wordpress/core-data';
|
|
@@ -96,17 +95,29 @@ const computeOutlineHeadings = ( blocks = [] ) => {
|
|
|
96
95
|
};
|
|
97
96
|
|
|
98
97
|
const isEmptyHeading = ( heading ) =>
|
|
99
|
-
! heading.attributes.content ||
|
|
98
|
+
! heading.attributes.content ||
|
|
99
|
+
heading.attributes.content.trim().length === 0;
|
|
100
100
|
|
|
101
|
-
export
|
|
102
|
-
blocks = [],
|
|
103
|
-
title,
|
|
101
|
+
export default function DocumentOutline( {
|
|
104
102
|
onSelect,
|
|
105
103
|
isTitleSupported,
|
|
106
104
|
hasOutlineItemsDisabled,
|
|
107
|
-
} )
|
|
108
|
-
const headings = computeOutlineHeadings( blocks );
|
|
105
|
+
} ) {
|
|
109
106
|
const { selectBlock } = useDispatch( blockEditorStore );
|
|
107
|
+
const { blocks, title } = useSelect( ( select ) => {
|
|
108
|
+
const { getBlocks } = select( blockEditorStore );
|
|
109
|
+
const { getEditedPostAttribute } = select( editorStore );
|
|
110
|
+
const { getPostType } = select( coreStore );
|
|
111
|
+
const postType = getPostType( getEditedPostAttribute( 'type' ) );
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
title: getEditedPostAttribute( 'title' ),
|
|
115
|
+
blocks: getBlocks(),
|
|
116
|
+
isTitleSupported: postType?.supports?.title ?? false,
|
|
117
|
+
};
|
|
118
|
+
} );
|
|
119
|
+
|
|
120
|
+
const headings = computeOutlineHeadings( blocks );
|
|
110
121
|
if ( headings.length < 1 ) {
|
|
111
122
|
return (
|
|
112
123
|
<div className="editor-document-outline has-no-headings">
|
|
@@ -194,19 +205,4 @@ export const DocumentOutline = ( {
|
|
|
194
205
|
</ul>
|
|
195
206
|
</div>
|
|
196
207
|
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export default compose(
|
|
200
|
-
withSelect( ( select ) => {
|
|
201
|
-
const { getBlocks } = select( blockEditorStore );
|
|
202
|
-
const { getEditedPostAttribute } = select( editorStore );
|
|
203
|
-
const { getPostType } = select( coreStore );
|
|
204
|
-
const postType = getPostType( getEditedPostAttribute( 'type' ) );
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
title: getEditedPostAttribute( 'title' ),
|
|
208
|
-
blocks: getBlocks(),
|
|
209
|
-
isTitleSupported: postType?.supports?.title ?? false,
|
|
210
|
-
};
|
|
211
|
-
} )
|
|
212
|
-
)( DocumentOutline );
|
|
208
|
+
}
|
|
@@ -11,15 +11,27 @@ import {
|
|
|
11
11
|
registerBlockType,
|
|
12
12
|
unregisterBlockType,
|
|
13
13
|
} from '@wordpress/blocks';
|
|
14
|
+
import { useSelect } from '@wordpress/data';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Internal dependencies
|
|
17
18
|
*/
|
|
18
|
-
import
|
|
19
|
+
import DocumentOutline from '../';
|
|
19
20
|
|
|
20
21
|
jest.mock( '@wordpress/block-editor', () => ( {
|
|
21
22
|
BlockTitle: () => 'Block Title',
|
|
22
23
|
} ) );
|
|
24
|
+
jest.mock( '@wordpress/data/src/components/use-select', () => jest.fn() );
|
|
25
|
+
|
|
26
|
+
function setupMockSelect( blocks ) {
|
|
27
|
+
useSelect.mockImplementation( ( mapSelect ) => {
|
|
28
|
+
return mapSelect( () => ( {
|
|
29
|
+
getBlocks: () => blocks,
|
|
30
|
+
getEditedPostAttribute: () => null,
|
|
31
|
+
getPostType: () => null,
|
|
32
|
+
} ) );
|
|
33
|
+
} );
|
|
34
|
+
}
|
|
23
35
|
|
|
24
36
|
describe( 'DocumentOutline', () => {
|
|
25
37
|
let paragraph, headingH1, headingH2, headingH3, nestedHeading;
|
|
@@ -77,6 +89,7 @@ describe( 'DocumentOutline', () => {
|
|
|
77
89
|
|
|
78
90
|
describe( 'no header blocks present', () => {
|
|
79
91
|
it( 'should not render when no blocks provided', () => {
|
|
92
|
+
setupMockSelect( [] );
|
|
80
93
|
render( <DocumentOutline /> );
|
|
81
94
|
|
|
82
95
|
expect( screen.queryByRole( 'list' ) ).not.toBeInTheDocument();
|
|
@@ -87,7 +100,8 @@ describe( 'DocumentOutline', () => {
|
|
|
87
100
|
// Set client IDs to a predictable value.
|
|
88
101
|
return { ...block, clientId: `clientId_${ index }` };
|
|
89
102
|
} );
|
|
90
|
-
|
|
103
|
+
setupMockSelect( blocks );
|
|
104
|
+
render( <DocumentOutline /> );
|
|
91
105
|
|
|
92
106
|
expect( screen.queryByRole( 'list' ) ).not.toBeInTheDocument();
|
|
93
107
|
} );
|
|
@@ -99,14 +113,16 @@ describe( 'DocumentOutline', () => {
|
|
|
99
113
|
// Set client IDs to a predictable value.
|
|
100
114
|
return { ...block, clientId: `clientId_${ index }` };
|
|
101
115
|
} );
|
|
102
|
-
|
|
116
|
+
setupMockSelect( blocks );
|
|
117
|
+
render( <DocumentOutline /> );
|
|
103
118
|
|
|
104
119
|
expect( screen.getByRole( 'list' ) ).toMatchSnapshot();
|
|
105
120
|
} );
|
|
106
121
|
|
|
107
122
|
it( 'should render an item when only one heading provided', () => {
|
|
108
123
|
const blocks = [ headingH2 ];
|
|
109
|
-
|
|
124
|
+
setupMockSelect( blocks );
|
|
125
|
+
render( <DocumentOutline /> );
|
|
110
126
|
|
|
111
127
|
const tableOfContentItem = within(
|
|
112
128
|
screen.getByRole( 'list' )
|
|
@@ -123,7 +139,8 @@ describe( 'DocumentOutline', () => {
|
|
|
123
139
|
headingH3,
|
|
124
140
|
paragraph,
|
|
125
141
|
];
|
|
126
|
-
|
|
142
|
+
setupMockSelect( blocks );
|
|
143
|
+
render( <DocumentOutline /> );
|
|
127
144
|
|
|
128
145
|
expect(
|
|
129
146
|
within( screen.getByRole( 'list' ) ).getAllByRole( 'listitem' )
|
|
@@ -137,7 +154,8 @@ describe( 'DocumentOutline', () => {
|
|
|
137
154
|
return { ...block, clientId: `clientId_${ index }` };
|
|
138
155
|
}
|
|
139
156
|
);
|
|
140
|
-
|
|
157
|
+
setupMockSelect( blocks );
|
|
158
|
+
render( <DocumentOutline /> );
|
|
141
159
|
|
|
142
160
|
expect( screen.getByRole( 'list' ) ).toMatchSnapshot();
|
|
143
161
|
} );
|
|
@@ -146,7 +164,8 @@ describe( 'DocumentOutline', () => {
|
|
|
146
164
|
describe( 'nested headings', () => {
|
|
147
165
|
it( 'should render even if the heading is nested', () => {
|
|
148
166
|
const blocks = [ headingH2, nestedHeading ];
|
|
149
|
-
|
|
167
|
+
setupMockSelect( blocks );
|
|
168
|
+
render( <DocumentOutline /> );
|
|
150
169
|
|
|
151
170
|
// Unnested heading and nested heading should appear as items.
|
|
152
171
|
const tableOfContentItems = within(
|
|
@@ -29,7 +29,7 @@ import { store as editorStore } from '../../store';
|
|
|
29
29
|
import EditorHistoryRedo from '../editor-history/redo';
|
|
30
30
|
import EditorHistoryUndo from '../editor-history/undo';
|
|
31
31
|
|
|
32
|
-
const {
|
|
32
|
+
const { useShowBlockTools } = unlock( blockEditorPrivateApis );
|
|
33
33
|
|
|
34
34
|
const preventDefault = ( event ) => {
|
|
35
35
|
event.preventDefault();
|
|
@@ -76,7 +76,7 @@ function DocumentTools( {
|
|
|
76
76
|
|
|
77
77
|
const isLargeViewport = useViewportMatch( 'medium' );
|
|
78
78
|
const isWideViewport = useViewportMatch( 'wide' );
|
|
79
|
-
const
|
|
79
|
+
const { showFixedToolbar } = useShowBlockTools();
|
|
80
80
|
|
|
81
81
|
/* translators: accessibility text for the editor toolbar */
|
|
82
82
|
const toolbarAriaLabel = __( 'Document tools' );
|
|
@@ -117,7 +117,7 @@ function DocumentTools( {
|
|
|
117
117
|
className
|
|
118
118
|
) }
|
|
119
119
|
aria-label={ toolbarAriaLabel }
|
|
120
|
-
shouldUseKeyboardFocusShortcut={ !
|
|
120
|
+
shouldUseKeyboardFocusShortcut={ ! showFixedToolbar }
|
|
121
121
|
variant="unstyled"
|
|
122
122
|
>
|
|
123
123
|
<div className="editor-document-tools__left">
|
|
@@ -94,7 +94,7 @@ export default function EntityTypeList( {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
return (
|
|
97
|
-
<PanelBody title={ entityLabel } initialOpen
|
|
97
|
+
<PanelBody title={ entityLabel } initialOpen>
|
|
98
98
|
<EntityDescription record={ firstRecord } count={ count } />
|
|
99
99
|
{ list.map( ( record ) => {
|
|
100
100
|
return (
|
|
@@ -4,29 +4,24 @@
|
|
|
4
4
|
import { useSelect } from '@wordpress/data';
|
|
5
5
|
import { store as coreStore } from '@wordpress/core-data';
|
|
6
6
|
import { useMemo, useState } from '@wordpress/element';
|
|
7
|
-
import { __ } from '@wordpress/i18n';
|
|
8
|
-
|
|
9
|
-
const TRANSLATED_SITE_PROPERTIES = {
|
|
10
|
-
title: __( 'Title' ),
|
|
11
|
-
description: __( 'Tagline' ),
|
|
12
|
-
site_logo: __( 'Logo' ),
|
|
13
|
-
site_icon: __( 'Icon' ),
|
|
14
|
-
show_on_front: __( 'Show on front' ),
|
|
15
|
-
page_on_front: __( 'Page on front' ),
|
|
16
|
-
posts_per_page: __( 'Maximum posts per page' ),
|
|
17
|
-
default_comment_status: __( 'Allow comments on new posts' ),
|
|
18
|
-
};
|
|
19
7
|
|
|
20
8
|
export const useIsDirty = () => {
|
|
21
|
-
const { editedEntities, siteEdits } = useSelect(
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
const { editedEntities, siteEdits, siteEntityConfig } = useSelect(
|
|
10
|
+
( select ) => {
|
|
11
|
+
const {
|
|
12
|
+
__experimentalGetDirtyEntityRecords,
|
|
13
|
+
getEntityRecordEdits,
|
|
14
|
+
getEntityConfig,
|
|
15
|
+
} = select( coreStore );
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
return {
|
|
18
|
+
editedEntities: __experimentalGetDirtyEntityRecords(),
|
|
19
|
+
siteEdits: getEntityRecordEdits( 'root', 'site' ),
|
|
20
|
+
siteEntityConfig: getEntityConfig( 'root', 'site' ),
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
[]
|
|
24
|
+
);
|
|
30
25
|
|
|
31
26
|
const dirtyEntityRecords = useMemo( () => {
|
|
32
27
|
// Remove site object and decouple into its edited pieces.
|
|
@@ -34,18 +29,19 @@ export const useIsDirty = () => {
|
|
|
34
29
|
( record ) => ! ( record.kind === 'root' && record.name === 'site' )
|
|
35
30
|
);
|
|
36
31
|
|
|
32
|
+
const siteEntityLabels = siteEntityConfig?.meta?.labels ?? {};
|
|
37
33
|
const editedSiteEntities = [];
|
|
38
34
|
for ( const property in siteEdits ) {
|
|
39
35
|
editedSiteEntities.push( {
|
|
40
36
|
kind: 'root',
|
|
41
37
|
name: 'site',
|
|
42
|
-
title:
|
|
38
|
+
title: siteEntityLabels[ property ] || property,
|
|
43
39
|
property,
|
|
44
40
|
} );
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
return [ ...editedEntitiesWithoutSite, ...editedSiteEntities ];
|
|
48
|
-
}, [ editedEntities, siteEdits ] );
|
|
44
|
+
}, [ editedEntities, siteEdits, siteEntityConfig ] );
|
|
49
45
|
|
|
50
46
|
// Unchecked entities to be ignored by save function.
|
|
51
47
|
const [ unselectedEntities, _setUnselectedEntities ] = useState( [] );
|
|
@@ -11,7 +11,10 @@ import {
|
|
|
11
11
|
} from '@wordpress/element';
|
|
12
12
|
import { store as coreStore } from '@wordpress/core-data';
|
|
13
13
|
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
__experimentalUseDialog as useDialog,
|
|
16
|
+
useInstanceId,
|
|
17
|
+
} from '@wordpress/compose';
|
|
15
18
|
import { store as noticesStore } from '@wordpress/notices';
|
|
16
19
|
|
|
17
20
|
/**
|
|
@@ -31,10 +34,17 @@ function identity( values ) {
|
|
|
31
34
|
return values;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
export default function EntitiesSavedStates( {
|
|
37
|
+
export default function EntitiesSavedStates( {
|
|
38
|
+
close,
|
|
39
|
+
renderDialog = undefined,
|
|
40
|
+
} ) {
|
|
35
41
|
const isDirtyProps = useIsDirty();
|
|
36
42
|
return (
|
|
37
|
-
<EntitiesSavedStatesExtensible
|
|
43
|
+
<EntitiesSavedStatesExtensible
|
|
44
|
+
close={ close }
|
|
45
|
+
renderDialog={ renderDialog }
|
|
46
|
+
{ ...isDirtyProps }
|
|
47
|
+
/>
|
|
38
48
|
);
|
|
39
49
|
}
|
|
40
50
|
|
|
@@ -44,6 +54,7 @@ export function EntitiesSavedStatesExtensible( {
|
|
|
44
54
|
onSave = identity,
|
|
45
55
|
saveEnabled: saveEnabledProp = undefined,
|
|
46
56
|
saveLabel = __( 'Save' ),
|
|
57
|
+
renderDialog = undefined,
|
|
47
58
|
|
|
48
59
|
dirtyEntityRecords,
|
|
49
60
|
isDirty,
|
|
@@ -183,12 +194,20 @@ export function EntitiesSavedStatesExtensible( {
|
|
|
183
194
|
const [ saveDialogRef, saveDialogProps ] = useDialog( {
|
|
184
195
|
onClose: () => dismissPanel(),
|
|
185
196
|
} );
|
|
197
|
+
const dialogLabel = useInstanceId( EntitiesSavedStatesExtensible, 'label' );
|
|
198
|
+
const dialogDescription = useInstanceId(
|
|
199
|
+
EntitiesSavedStatesExtensible,
|
|
200
|
+
'description'
|
|
201
|
+
);
|
|
186
202
|
|
|
187
203
|
return (
|
|
188
204
|
<div
|
|
189
205
|
ref={ saveDialogRef }
|
|
190
206
|
{ ...saveDialogProps }
|
|
191
207
|
className="entities-saved-states__panel"
|
|
208
|
+
role={ renderDialog ? 'dialog' : undefined }
|
|
209
|
+
aria-labelledby={ renderDialog ? dialogLabel : undefined }
|
|
210
|
+
aria-describedby={ renderDialog ? dialogDescription : undefined }
|
|
192
211
|
>
|
|
193
212
|
<Flex className="entities-saved-states__panel-header" gap={ 2 }>
|
|
194
213
|
<FlexItem
|
|
@@ -197,6 +216,7 @@ export function EntitiesSavedStatesExtensible( {
|
|
|
197
216
|
ref={ saveButtonRef }
|
|
198
217
|
variant="primary"
|
|
199
218
|
disabled={ ! saveEnabled }
|
|
219
|
+
__experimentalIsFocusable
|
|
200
220
|
onClick={ saveCheckedEntities }
|
|
201
221
|
className="editor-entities-saved-states__save-button"
|
|
202
222
|
>
|
|
@@ -213,11 +233,16 @@ export function EntitiesSavedStatesExtensible( {
|
|
|
213
233
|
</Flex>
|
|
214
234
|
|
|
215
235
|
<div className="entities-saved-states__text-prompt">
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
236
|
+
<div
|
|
237
|
+
className="entities-saved-states__text-prompt--header-wrapper"
|
|
238
|
+
id={ renderDialog ? dialogLabel : undefined }
|
|
239
|
+
>
|
|
240
|
+
<strong className="entities-saved-states__text-prompt--header">
|
|
241
|
+
{ __( 'Are you ready to save?' ) }
|
|
242
|
+
</strong>
|
|
243
|
+
{ additionalPrompt }
|
|
244
|
+
</div>
|
|
245
|
+
<p id={ renderDialog ? dialogDescription : undefined }>
|
|
221
246
|
{ isDirty
|
|
222
247
|
? createInterpolateElement(
|
|
223
248
|
sprintf(
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
5
|
+
import Clipboard from '@react-native-clipboard/clipboard';
|
|
6
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* WordPress dependencies
|
|
10
|
+
*/
|
|
11
|
+
import { Component } from '@wordpress/element';
|
|
12
|
+
import { __ } from '@wordpress/i18n';
|
|
13
|
+
import { select } from '@wordpress/data';
|
|
14
|
+
import { logException } from '@wordpress/react-native-bridge';
|
|
15
|
+
import {
|
|
16
|
+
usePreferredColorSchemeStyle,
|
|
17
|
+
withPreferredColorScheme,
|
|
18
|
+
} from '@wordpress/compose';
|
|
19
|
+
import { warning } from '@wordpress/icons';
|
|
20
|
+
import { Icon } from '@wordpress/components';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Internal dependencies
|
|
24
|
+
*/
|
|
25
|
+
import { store as editorStore } from '../../store';
|
|
26
|
+
import styles from './style.scss';
|
|
27
|
+
|
|
28
|
+
function getContent() {
|
|
29
|
+
try {
|
|
30
|
+
// While `select` in a component is generally discouraged, it is
|
|
31
|
+
// used here because it (a) reduces the chance of data loss in the
|
|
32
|
+
// case of additional errors by performing a direct retrieval and
|
|
33
|
+
// (b) avoids the performance cost associated with unnecessary
|
|
34
|
+
// content serialization throughout the lifetime of a non-erroring
|
|
35
|
+
// application.
|
|
36
|
+
return select( editorStore ).getEditedPostContent();
|
|
37
|
+
} catch ( error ) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function CopyButton( {
|
|
41
|
+
text,
|
|
42
|
+
label,
|
|
43
|
+
accessibilityLabel,
|
|
44
|
+
accessibilityHint,
|
|
45
|
+
secondary = false,
|
|
46
|
+
} ) {
|
|
47
|
+
const containerStyle = usePreferredColorSchemeStyle(
|
|
48
|
+
styles[ 'copy-button__container' ],
|
|
49
|
+
styles[ 'copy-button__container--dark' ]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const containerSecondaryStyle = usePreferredColorSchemeStyle(
|
|
53
|
+
styles[ 'copy-button__container--secondary' ],
|
|
54
|
+
styles[ 'copy-button__container--secondary-dark' ]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const textStyle = usePreferredColorSchemeStyle(
|
|
58
|
+
styles[ 'copy-button__text' ],
|
|
59
|
+
styles[ 'copy-button__text--dark' ]
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const textSecondaryStyle = usePreferredColorSchemeStyle(
|
|
63
|
+
styles[ 'copy-button__text--secondary' ],
|
|
64
|
+
styles[ 'copy-button__text--secondary-dark' ]
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<TouchableOpacity
|
|
69
|
+
activeOpacity={ 0.5 }
|
|
70
|
+
accessibilityLabel={ accessibilityLabel }
|
|
71
|
+
style={ [ containerStyle, secondary && containerSecondaryStyle ] }
|
|
72
|
+
accessibilityRole={ 'button' }
|
|
73
|
+
accessibilityHint={ accessibilityHint }
|
|
74
|
+
onPress={ () => {
|
|
75
|
+
Clipboard.setString(
|
|
76
|
+
typeof text === 'function' ? text() : text || ''
|
|
77
|
+
);
|
|
78
|
+
} }
|
|
79
|
+
>
|
|
80
|
+
<Text style={ [ textStyle, secondary && textSecondaryStyle ] }>
|
|
81
|
+
{ label }
|
|
82
|
+
</Text>
|
|
83
|
+
</TouchableOpacity>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
class ErrorBoundary extends Component {
|
|
88
|
+
constructor() {
|
|
89
|
+
super( ...arguments );
|
|
90
|
+
|
|
91
|
+
this.state = {
|
|
92
|
+
error: null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
componentDidCatch( error ) {
|
|
97
|
+
logException( error, {
|
|
98
|
+
context: {
|
|
99
|
+
component_stack: error.componentStack,
|
|
100
|
+
},
|
|
101
|
+
isHandled: true,
|
|
102
|
+
handledBy: 'Editor-level Error Boundary',
|
|
103
|
+
} );
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static getDerivedStateFromError( error ) {
|
|
107
|
+
return { error };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
render() {
|
|
111
|
+
const { error } = this.state;
|
|
112
|
+
if ( ! error ) {
|
|
113
|
+
return this.props.children;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { getStylesFromColorScheme } = this.props;
|
|
117
|
+
|
|
118
|
+
const iconContainerStyle = getStylesFromColorScheme(
|
|
119
|
+
styles[ 'error-boundary__icon-container' ],
|
|
120
|
+
styles[ 'error-boundary__icon-container--dark' ]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const titleStyle = getStylesFromColorScheme(
|
|
124
|
+
styles[ 'error-boundary__title' ],
|
|
125
|
+
styles[ 'error-boundary__title--dark' ]
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const messageStyle = getStylesFromColorScheme(
|
|
129
|
+
styles[ 'error-boundary__message' ],
|
|
130
|
+
styles[ 'error-boundary__message--dark' ]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<SafeAreaView>
|
|
135
|
+
<ScrollView
|
|
136
|
+
style={ styles[ 'error-boundary__scroll' ] }
|
|
137
|
+
contentContainerStyle={
|
|
138
|
+
styles[ 'error-boundary__scroll-container' ]
|
|
139
|
+
}
|
|
140
|
+
>
|
|
141
|
+
<View style={ styles[ 'error-boundary__container' ] }>
|
|
142
|
+
<View style={ iconContainerStyle }>
|
|
143
|
+
<Icon
|
|
144
|
+
icon={ warning }
|
|
145
|
+
{ ...styles[ 'error-boundary__icon' ] }
|
|
146
|
+
/>
|
|
147
|
+
</View>
|
|
148
|
+
<Text style={ titleStyle }>
|
|
149
|
+
{ __(
|
|
150
|
+
'The editor has encountered an unexpected error'
|
|
151
|
+
) }
|
|
152
|
+
</Text>
|
|
153
|
+
<Text style={ messageStyle }>
|
|
154
|
+
{ __(
|
|
155
|
+
'You can copy your post text in case your content is impacted. Copy error details to debug and share with support.'
|
|
156
|
+
) }
|
|
157
|
+
</Text>
|
|
158
|
+
<View
|
|
159
|
+
style={
|
|
160
|
+
styles[ 'error-boundary__actions-container' ]
|
|
161
|
+
}
|
|
162
|
+
>
|
|
163
|
+
<CopyButton
|
|
164
|
+
label={ __( 'Copy post text' ) }
|
|
165
|
+
accessibilityLabel={ __(
|
|
166
|
+
'Button to copy post text'
|
|
167
|
+
) }
|
|
168
|
+
accessibilityHint={ __(
|
|
169
|
+
'Tap here to copy post text'
|
|
170
|
+
) }
|
|
171
|
+
text={ getContent }
|
|
172
|
+
/>
|
|
173
|
+
<CopyButton
|
|
174
|
+
label={ __( 'Copy error details' ) }
|
|
175
|
+
accessibilityLabel={ __(
|
|
176
|
+
'Button to copy error details'
|
|
177
|
+
) }
|
|
178
|
+
accessibilityHint={ __(
|
|
179
|
+
'Tap here to copy error details'
|
|
180
|
+
) }
|
|
181
|
+
text={ error.stack }
|
|
182
|
+
secondary
|
|
183
|
+
/>
|
|
184
|
+
</View>
|
|
185
|
+
</View>
|
|
186
|
+
</ScrollView>
|
|
187
|
+
</SafeAreaView>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export default withPreferredColorScheme( ErrorBoundary );
|