@wordpress/edit-post 8.0.1 → 8.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 (39) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/layout/index.js +88 -48
  3. package/build/components/layout/index.js.map +1 -1
  4. package/build/components/welcome-guide/index.js +5 -7
  5. package/build/components/welcome-guide/index.js.map +1 -1
  6. package/build/index.js +8 -6
  7. package/build/index.js.map +1 -1
  8. package/build/store/index.js +3 -0
  9. package/build/store/index.js.map +1 -1
  10. package/build/store/private-selectors.js +56 -0
  11. package/build/store/private-selectors.js.map +1 -0
  12. package/build/store/selectors.js +6 -43
  13. package/build/store/selectors.js.map +1 -1
  14. package/build-module/components/layout/index.js +90 -51
  15. package/build-module/components/layout/index.js.map +1 -1
  16. package/build-module/components/welcome-guide/index.js +5 -7
  17. package/build-module/components/welcome-guide/index.js.map +1 -1
  18. package/build-module/index.js +9 -7
  19. package/build-module/index.js.map +1 -1
  20. package/build-module/store/index.js +3 -0
  21. package/build-module/store/index.js.map +1 -1
  22. package/build-module/store/private-selectors.js +49 -0
  23. package/build-module/store/private-selectors.js.map +1 -0
  24. package/build-module/store/selectors.js +6 -43
  25. package/build-module/store/selectors.js.map +1 -1
  26. package/package.json +31 -30
  27. package/src/components/layout/index.js +130 -66
  28. package/src/components/welcome-guide/index.js +15 -15
  29. package/src/index.js +10 -8
  30. package/src/store/index.js +3 -1
  31. package/src/store/private-selectors.js +52 -0
  32. package/src/store/selectors.js +10 -61
  33. package/src/test/__snapshots__/editor.native.js.snap +12 -0
  34. package/src/test/editor.native.js +34 -0
  35. package/build/editor.js +0 -100
  36. package/build/editor.js.map +0 -1
  37. package/build-module/editor.js +0 -93
  38. package/build-module/editor.js.map +0 -1
  39. package/src/editor.js +0 -118
@@ -12,6 +12,8 @@ import {
12
12
  UnsavedChangesWarning,
13
13
  EditorKeyboardShortcutsRegister,
14
14
  EditorSnackbars,
15
+ ErrorBoundary,
16
+ PostLockedModal,
15
17
  store as editorStore,
16
18
  privateApis as editorPrivateApis,
17
19
  } from '@wordpress/editor';
@@ -25,15 +27,22 @@ import { __, sprintf } from '@wordpress/i18n';
25
27
  import { useCallback, useMemo } from '@wordpress/element';
26
28
  import { store as noticesStore } from '@wordpress/notices';
27
29
  import { store as preferencesStore } from '@wordpress/preferences';
28
- import { privateApis as commandsPrivateApis } from '@wordpress/commands';
30
+ import {
31
+ CommandMenu,
32
+ privateApis as commandsPrivateApis,
33
+ } from '@wordpress/commands';
29
34
  import { privateApis as coreCommandsPrivateApis } from '@wordpress/core-commands';
30
35
  import { privateApis as blockLibraryPrivateApis } from '@wordpress/block-library';
31
36
  import { addQueryArgs } from '@wordpress/url';
37
+ import { decodeEntities } from '@wordpress/html-entities';
38
+ import { store as coreStore } from '@wordpress/core-data';
39
+ import { SlotFillProvider } from '@wordpress/components';
32
40
 
33
41
  /**
34
42
  * Internal dependencies
35
43
  */
36
44
  import BackButton from '../back-button';
45
+ import EditorInitialization from '../editor-initialization';
37
46
  import EditPostKeyboardShortcuts from '../keyboard-shortcuts';
38
47
  import InitPatternModal from '../init-pattern-modal';
39
48
  import BrowserURL from '../browser-url';
@@ -45,12 +54,12 @@ import { unlock } from '../../lock-unlock';
45
54
  import useEditPostCommands from '../../commands/use-commands';
46
55
  import { usePaddingAppender } from './use-padding-appender';
47
56
  import { useShouldIframe } from './use-should-iframe';
57
+ import useNavigateToEntityRecord from '../../hooks/use-navigate-to-entity-record';
48
58
 
49
59
  const { getLayoutStyles } = unlock( blockEditorPrivateApis );
50
60
  const { useCommands } = unlock( coreCommandsPrivateApis );
51
61
  const { useCommandContext } = unlock( commandsPrivateApis );
52
- const { EditorInterface, FullscreenMode, Sidebar } =
53
- unlock( editorPrivateApis );
62
+ const { Editor, FullscreenMode } = unlock( editorPrivateApis );
54
63
  const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis );
55
64
  const DESIGN_POST_TYPES = [
56
65
  'wp_template',
@@ -91,7 +100,7 @@ function useEditorStyles() {
91
100
  ) ?? [];
92
101
 
93
102
  const defaultEditorStyles = [
94
- ...editorSettings.defaultEditorStyles,
103
+ ...( editorSettings?.defaultEditorStyles ?? [] ),
95
104
  ...presetStyles,
96
105
  ];
97
106
 
@@ -144,12 +153,26 @@ function useEditorStyles() {
144
153
  ] );
145
154
  }
146
155
 
147
- function Layout( { initialPost } ) {
156
+ function Layout( {
157
+ postId: initialPostId,
158
+ postType: initialPostType,
159
+ settings,
160
+ initialEdits,
161
+ } ) {
148
162
  useCommands();
149
163
  useEditPostCommands();
150
164
  const paddingAppenderRef = usePaddingAppender();
151
165
  const shouldIframe = useShouldIframe();
152
166
  const { createErrorNotice } = useDispatch( noticesStore );
167
+ const {
168
+ currentPost,
169
+ onNavigateToEntityRecord,
170
+ onNavigateToPreviousEntityRecord,
171
+ } = useNavigateToEntityRecord(
172
+ initialPostId,
173
+ initialPostType,
174
+ 'post-only'
175
+ );
153
176
  const {
154
177
  mode,
155
178
  isFullscreenActive,
@@ -161,35 +184,61 @@ function Layout( { initialPost } ) {
161
184
  hasHistory,
162
185
  isEditingTemplate,
163
186
  isWelcomeGuideVisible,
164
- } = useSelect( ( select ) => {
165
- const { get } = select( preferencesStore );
166
- const { getEditorSettings } = select( editorStore );
167
- const { isFeatureActive } = select( editPostStore );
187
+ templateId,
188
+ } = useSelect(
189
+ ( select ) => {
190
+ const { get } = select( preferencesStore );
191
+ const { isFeatureActive, getEditedPostTemplateId } = unlock(
192
+ select( editPostStore )
193
+ );
194
+ const { canUser, getPostType } = select( coreStore );
168
195
 
169
- return {
170
- mode: select( editorStore ).getEditorMode(),
171
- isFullscreenActive:
172
- select( editPostStore ).isFeatureActive( 'fullscreenMode' ),
173
- hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(),
174
- hasBlockSelected:
175
- !! select( blockEditorStore ).getBlockSelectionStart(),
176
- showIconLabels: get( 'core', 'showIconLabels' ),
177
- isDistractionFree: get( 'core', 'distractionFree' ),
178
- showMetaBoxes:
179
- select( editorStore ).getRenderingMode() === 'post-only',
180
- hasHistory: !! getEditorSettings().onNavigateToPreviousEntityRecord,
181
- isEditingTemplate:
182
- select( editorStore ).getCurrentPostType() === 'wp_template',
183
- isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ),
184
- };
185
- }, [] );
196
+ const supportsTemplateMode = settings.supportsTemplateMode;
197
+ const isViewable =
198
+ getPostType( currentPost.postType )?.viewable ?? false;
199
+ const canViewTemplate = canUser( 'read', 'templates' );
200
+
201
+ return {
202
+ mode: select( editorStore ).getEditorMode(),
203
+ isFullscreenActive:
204
+ select( editPostStore ).isFeatureActive( 'fullscreenMode' ),
205
+ hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(),
206
+ hasBlockSelected:
207
+ !! select( blockEditorStore ).getBlockSelectionStart(),
208
+ showIconLabels: get( 'core', 'showIconLabels' ),
209
+ isDistractionFree: get( 'core', 'distractionFree' ),
210
+ showMetaBoxes:
211
+ select( editorStore ).getRenderingMode() === 'post-only',
212
+ isEditingTemplate:
213
+ select( editorStore ).getCurrentPostType() ===
214
+ 'wp_template',
215
+ isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ),
216
+ templateId:
217
+ supportsTemplateMode &&
218
+ isViewable &&
219
+ canViewTemplate &&
220
+ currentPost.postType !== 'wp_template'
221
+ ? getEditedPostTemplateId()
222
+ : null,
223
+ };
224
+ },
225
+ [ settings.supportsTemplateMode, currentPost.postType ]
226
+ );
186
227
 
187
228
  // Set the right context for the command palette
188
229
  const commandContext = hasBlockSelected
189
230
  ? 'block-selection-edit'
190
231
  : 'entity-edit';
191
232
  useCommandContext( commandContext );
192
-
233
+ const editorSettings = useMemo(
234
+ () => ( {
235
+ ...settings,
236
+ onNavigateToEntityRecord,
237
+ onNavigateToPreviousEntityRecord,
238
+ defaultRenderingMode: 'post-only',
239
+ } ),
240
+ [ settings, onNavigateToEntityRecord, onNavigateToPreviousEntityRecord ]
241
+ );
193
242
  const styles = useEditorStyles();
194
243
 
195
244
  // We need to add the show-icon-labels class to the body element so it is applied to modals.
@@ -240,7 +289,7 @@ function Layout( { initialPost } ) {
240
289
  sprintf(
241
290
  // translators: %s: Title of the created post e.g: "Post 1".
242
291
  __( '"%s" successfully created.' ),
243
- title
292
+ decodeEntities( title )
244
293
  ),
245
294
  {
246
295
  type: 'snackbar',
@@ -267,48 +316,63 @@ function Layout( { initialPost } ) {
267
316
  [ createSuccessNotice ]
268
317
  );
269
318
 
319
+ const initialPost = useMemo( () => {
320
+ return {
321
+ type: initialPostType,
322
+ id: initialPostId,
323
+ };
324
+ }, [ initialPostType, initialPostId ] );
270
325
  return (
271
- <>
272
- <FullscreenMode isActive={ isFullscreenActive } />
273
- <BrowserURL hasHistory={ hasHistory } />
274
- <UnsavedChangesWarning />
275
- <AutosaveMonitor />
276
- <LocalAutosaveMonitor />
277
- <EditPostKeyboardShortcuts />
278
- <EditorKeyboardShortcutsRegister />
279
- <BlockKeyboardShortcuts />
280
- <WelcomeGuide />
281
- <InitPatternModal />
282
- <PluginArea onError={ onPluginAreaError } />
283
- { ! isDistractionFree && (
284
- <Sidebar
326
+ <SlotFillProvider>
327
+ <ErrorBoundary>
328
+ <CommandMenu />
329
+ <WelcomeGuide postType={ currentPost.postType } />
330
+ <Editor
331
+ settings={ editorSettings }
332
+ initialEdits={ initialEdits }
333
+ postType={ currentPost.postType }
334
+ postId={ currentPost.postId }
335
+ templateId={ templateId }
336
+ className={ className }
337
+ styles={ styles }
338
+ forceIsDirty={ hasActiveMetaboxes }
339
+ contentRef={ paddingAppenderRef }
340
+ disableIframe={ ! shouldIframe }
341
+ // We should auto-focus the canvas (title) on load.
342
+ // eslint-disable-next-line jsx-a11y/no-autofocus
343
+ autoFocus={ ! isWelcomeGuideVisible }
285
344
  onActionPerformed={ onActionPerformed }
286
- extraPanels={
345
+ extraSidebarPanels={
287
346
  ! isEditingTemplate && <MetaBoxes location="side" />
288
347
  }
289
- />
290
- ) }
291
- <PostEditorMoreMenu />
292
- <BackButton initialPost={ initialPost } />
293
- <EditorSnackbars />
294
- <EditorInterface
295
- className={ className }
296
- styles={ styles }
297
- forceIsDirty={ hasActiveMetaboxes }
298
- contentRef={ paddingAppenderRef }
299
- disableIframe={ ! shouldIframe }
300
- // We should auto-focus the canvas (title) on load.
301
- // eslint-disable-next-line jsx-a11y/no-autofocus
302
- autoFocus={ ! isWelcomeGuideVisible }
303
- >
304
- { ! isDistractionFree && showMetaBoxes && (
305
- <div className="edit-post-layout__metaboxes">
306
- <MetaBoxes location="normal" />
307
- <MetaBoxes location="advanced" />
308
- </div>
309
- ) }
310
- </EditorInterface>
311
- </>
348
+ extraContent={
349
+ ! isDistractionFree &&
350
+ showMetaBoxes && (
351
+ <div className="edit-post-layout__metaboxes">
352
+ <MetaBoxes location="normal" />
353
+ <MetaBoxes location="advanced" />
354
+ </div>
355
+ )
356
+ }
357
+ >
358
+ <PostLockedModal />
359
+ <EditorInitialization />
360
+ <FullscreenMode isActive={ isFullscreenActive } />
361
+ <BrowserURL hasHistory={ hasHistory } />
362
+ <UnsavedChangesWarning />
363
+ <AutosaveMonitor />
364
+ <LocalAutosaveMonitor />
365
+ <EditPostKeyboardShortcuts />
366
+ <EditorKeyboardShortcutsRegister />
367
+ <BlockKeyboardShortcuts />
368
+ <InitPatternModal />
369
+ <PluginArea onError={ onPluginAreaError } />
370
+ <PostEditorMoreMenu />
371
+ <BackButton initialPost={ initialPost } />
372
+ <EditorSnackbars />
373
+ </Editor>
374
+ </ErrorBoundary>
375
+ </SlotFillProvider>
312
376
  );
313
377
  }
314
378
 
@@ -2,7 +2,6 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { useSelect } from '@wordpress/data';
5
- import { store as editorStore } from '@wordpress/editor';
6
5
 
7
6
  /**
8
7
  * Internal dependencies
@@ -11,21 +10,22 @@ import WelcomeGuideDefault from './default';
11
10
  import WelcomeGuideTemplate from './template';
12
11
  import { store as editPostStore } from '../../store';
13
12
 
14
- export default function WelcomeGuide() {
15
- const { isActive, isEditingTemplate } = useSelect( ( select ) => {
16
- const { isFeatureActive } = select( editPostStore );
17
- const { getCurrentPostType } = select( editorStore );
18
- const _isEditingTemplate = getCurrentPostType() === 'wp_template';
13
+ export default function WelcomeGuide( { postType } ) {
14
+ const { isActive, isEditingTemplate } = useSelect(
15
+ ( select ) => {
16
+ const { isFeatureActive } = select( editPostStore );
17
+ const _isEditingTemplate = postType === 'wp_template';
18
+ const feature = _isEditingTemplate
19
+ ? 'welcomeGuideTemplate'
20
+ : 'welcomeGuide';
19
21
 
20
- const feature = _isEditingTemplate
21
- ? 'welcomeGuideTemplate'
22
- : 'welcomeGuide';
23
-
24
- return {
25
- isActive: isFeatureActive( feature ),
26
- isEditingTemplate: _isEditingTemplate,
27
- };
28
- }, [] );
22
+ return {
23
+ isActive: isFeatureActive( feature ),
24
+ isEditingTemplate: _isEditingTemplate,
25
+ };
26
+ },
27
+ [ postType ]
28
+ );
29
29
 
30
30
  if ( ! isActive ) {
31
31
  return null;
package/src/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  __experimentalRegisterExperimentalCoreBlocks,
8
8
  } from '@wordpress/block-library';
9
9
  import deprecated from '@wordpress/deprecated';
10
- import { createRoot } from '@wordpress/element';
10
+ import { createRoot, StrictMode } from '@wordpress/element';
11
11
  import { dispatch, select } from '@wordpress/data';
12
12
  import { store as preferencesStore } from '@wordpress/preferences';
13
13
  import {
@@ -22,7 +22,7 @@ import {
22
22
  /**
23
23
  * Internal dependencies
24
24
  */
25
- import Editor from './editor';
25
+ import Layout from './components/layout';
26
26
  import { unlock } from './lock-unlock';
27
27
 
28
28
  const { BackButton: __experimentalMainDashboardButton } =
@@ -137,12 +137,14 @@ export function initializeEditor(
137
137
  window.addEventListener( 'drop', ( e ) => e.preventDefault(), false );
138
138
 
139
139
  root.render(
140
- <Editor
141
- settings={ settings }
142
- postId={ postId }
143
- postType={ postType }
144
- initialEdits={ initialEdits }
145
- />
140
+ <StrictMode>
141
+ <Layout
142
+ settings={ settings }
143
+ postId={ postId }
144
+ postType={ postType }
145
+ initialEdits={ initialEdits }
146
+ />
147
+ </StrictMode>
146
148
  );
147
149
 
148
150
  return root;
@@ -9,7 +9,9 @@ import { createReduxStore, register } from '@wordpress/data';
9
9
  import reducer from './reducer';
10
10
  import * as actions from './actions';
11
11
  import * as selectors from './selectors';
12
+ import * as privateSelectors from './private-selectors';
12
13
  import { STORE_NAME } from './constants';
14
+ import { unlock } from '../lock-unlock';
13
15
 
14
16
  /**
15
17
  * Store definition for the edit post namespace.
@@ -23,5 +25,5 @@ export const store = createReduxStore( STORE_NAME, {
23
25
  actions,
24
26
  selectors,
25
27
  } );
26
-
27
28
  register( store );
29
+ unlock( store ).registerPrivateSelectors( privateSelectors );
@@ -0,0 +1,52 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { createRegistrySelector } from '@wordpress/data';
5
+ import { store as coreStore } from '@wordpress/core-data';
6
+ import { store as editorStore } from '@wordpress/editor';
7
+
8
+ export const getEditedPostTemplateId = createRegistrySelector(
9
+ ( select ) => () => {
10
+ const {
11
+ id: postId,
12
+ type: postType,
13
+ slug,
14
+ } = select( editorStore ).getCurrentPost();
15
+ const { getSite, getEntityRecords } = select( coreStore );
16
+ const siteSettings = getSite();
17
+ // First check if the current page is set as the posts page.
18
+ const isPostsPage = +postId === siteSettings?.page_for_posts;
19
+ if ( isPostsPage ) {
20
+ return select( coreStore ).getDefaultTemplateId( { slug: 'home' } );
21
+ }
22
+ const currentTemplate =
23
+ select( editorStore ).getEditedPostAttribute( 'template' );
24
+ if ( currentTemplate ) {
25
+ const templateWithSameSlug = getEntityRecords(
26
+ 'postType',
27
+ 'wp_template',
28
+ { per_page: -1 }
29
+ )?.find( ( template ) => template.slug === currentTemplate );
30
+ if ( ! templateWithSameSlug ) {
31
+ return templateWithSameSlug;
32
+ }
33
+ return templateWithSameSlug.id;
34
+ }
35
+ let slugToCheck;
36
+ // In `draft` status we might not have a slug available, so we use the `single`
37
+ // post type templates slug(ex page, single-post, single-product etc..).
38
+ // Pages do not need the `single` prefix in the slug to be prioritized
39
+ // through template hierarchy.
40
+ if ( slug ) {
41
+ slugToCheck =
42
+ postType === 'page'
43
+ ? `${ postType }-${ slug }`
44
+ : `single-${ postType }-${ slug }`;
45
+ } else {
46
+ slugToCheck = postType === 'page' ? 'page' : `single-${ postType }`;
47
+ }
48
+ return select( coreStore ).getDefaultTemplateId( {
49
+ slug: slugToCheck,
50
+ } );
51
+ }
52
+ );
@@ -14,6 +14,7 @@ import deprecated from '@wordpress/deprecated';
14
14
  * Internal dependencies
15
15
  */
16
16
  import { unlock } from '../lock-unlock';
17
+ import { getEditedPostTemplateId } from './private-selectors';
17
18
 
18
19
  const { interfaceStore } = unlock( editorPrivateApis );
19
20
  const EMPTY_ARRAY = [];
@@ -555,67 +556,15 @@ export function areMetaBoxesInitialized( state ) {
555
556
  * @return {Object?} Post Template.
556
557
  */
557
558
  export const getEditedPostTemplate = createRegistrySelector(
558
- ( select ) => () => {
559
- const {
560
- id: postId,
561
- type: postType,
562
- slug,
563
- } = select( editorStore ).getCurrentPost();
564
- const { getSite, getEditedEntityRecord, getEntityRecords } =
565
- select( coreStore );
566
- const siteSettings = getSite();
567
- // First check if the current page is set as the posts page.
568
- const isPostsPage = +postId === siteSettings?.page_for_posts;
569
- if ( isPostsPage ) {
570
- const defaultTemplateId = select( coreStore ).getDefaultTemplateId(
571
- { slug: 'home' }
572
- );
573
- return getEditedEntityRecord(
574
- 'postType',
575
- 'wp_template',
576
- defaultTemplateId
577
- );
578
- }
579
- const currentTemplate =
580
- select( editorStore ).getEditedPostAttribute( 'template' );
581
- if ( currentTemplate ) {
582
- const templateWithSameSlug = getEntityRecords(
583
- 'postType',
584
- 'wp_template',
585
- { per_page: -1 }
586
- )?.find( ( template ) => template.slug === currentTemplate );
587
- if ( ! templateWithSameSlug ) {
588
- return templateWithSameSlug;
589
- }
590
- return getEditedEntityRecord(
591
- 'postType',
592
- 'wp_template',
593
- templateWithSameSlug.id
594
- );
595
- }
596
- let slugToCheck;
597
- // In `draft` status we might not have a slug available, so we use the `single`
598
- // post type templates slug(ex page, single-post, single-product etc..).
599
- // Pages do not need the `single` prefix in the slug to be prioritized
600
- // through template hierarchy.
601
- if ( slug ) {
602
- slugToCheck =
603
- postType === 'page'
604
- ? `${ postType }-${ slug }`
605
- : `single-${ postType }-${ slug }`;
606
- } else {
607
- slugToCheck = postType === 'page' ? 'page' : `single-${ postType }`;
559
+ ( select ) => ( state ) => {
560
+ const templateId = getEditedPostTemplateId( state );
561
+ if ( ! templateId ) {
562
+ return undefined;
608
563
  }
609
- const defaultTemplateId = select( coreStore ).getDefaultTemplateId( {
610
- slug: slugToCheck,
611
- } );
612
-
613
- return defaultTemplateId
614
- ? select( coreStore ).getEditedEntityRecord(
615
- 'postType',
616
- 'wp_template',
617
- defaultTemplateId
618
- )
619
- : null;
564
+ return select( coreStore ).getEditedEntityRecord(
565
+ 'postType',
566
+ 'wp_template',
567
+ templateId
568
+ );
620
569
  }
621
570
  );
@@ -1,5 +1,17 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`Editor adds empty image block when pasting unsupported HTML local image path 1`] = `
4
+ "<!-- wp:image -->
5
+ <figure class="wp-block-image"><img src="" alt=""/></figure>
6
+ <!-- /wp:image -->"
7
+ `;
8
+
9
+ exports[`Editor adds image block when pasting HTML local image path 1`] = `
10
+ "<!-- wp:image -->
11
+ <figure class="wp-block-image"><img src="file:///path/to/file.png" alt=""/></figure>
12
+ <!-- /wp:image -->"
13
+ `;
14
+
3
15
  exports[`Editor appends media correctly for allowed types 1`] = `
4
16
  "<!-- wp:image -->
5
17
  <figure class="wp-block-image"><img src="https://test-site.files.wordpress.com/local-image-1.jpeg" alt=""/></figure>
@@ -9,8 +9,10 @@ import {
9
9
  getEditorHtml,
10
10
  getEditorTitle,
11
11
  initializeEditor,
12
+ pasteIntoRichText,
12
13
  screen,
13
14
  setupCoreBlocks,
15
+ within,
14
16
  } from 'test/helpers';
15
17
  import { BackHandler } from 'react-native';
16
18
 
@@ -98,6 +100,38 @@ describe( 'Editor', () => {
98
100
  } );
99
101
  } );
100
102
 
103
+ it( 'adds empty image block when pasting unsupported HTML local image path', async () => {
104
+ await initializeEditor();
105
+ await addBlock( screen, 'Paragraph' );
106
+
107
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
108
+ fireEvent.press( paragraphBlock );
109
+ const paragraphTextInput =
110
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
111
+
112
+ pasteIntoRichText( paragraphTextInput, {
113
+ text: '<div><img src="file:LOW-RES.png"></div>',
114
+ } );
115
+
116
+ expect( getEditorHtml() ).toMatchSnapshot();
117
+ } );
118
+
119
+ it( 'adds image block when pasting HTML local image path', async () => {
120
+ await initializeEditor();
121
+ await addBlock( screen, 'Paragraph' );
122
+
123
+ const paragraphBlock = getBlock( screen, 'Paragraph' );
124
+ fireEvent.press( paragraphBlock );
125
+ const paragraphTextInput =
126
+ within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
127
+
128
+ pasteIntoRichText( paragraphTextInput, {
129
+ files: [ 'file:///path/to/file.png' ],
130
+ } );
131
+
132
+ expect( getEditorHtml() ).toMatchSnapshot();
133
+ } );
134
+
101
135
  it( 'appends media correctly for allowed types', async () => {
102
136
  // Arrange
103
137
  requestMediaImport