@wordpress/editor 13.14.0 → 13.16.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 (125) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/build/components/entities-saved-states/entity-record-item.js +3 -37
  3. package/build/components/entities-saved-states/entity-record-item.js.map +1 -1
  4. package/build/components/entities-saved-states/entity-type-list.js +2 -4
  5. package/build/components/entities-saved-states/entity-type-list.js.map +1 -1
  6. package/build/components/entities-saved-states/index.js +0 -1
  7. package/build/components/entities-saved-states/index.js.map +1 -1
  8. package/build/components/global-keyboard-shortcuts/{save-shortcut.js → index.js} +13 -25
  9. package/build/components/global-keyboard-shortcuts/index.js.map +1 -0
  10. package/build/components/index.js +32 -19
  11. package/build/components/index.js.map +1 -1
  12. package/build/components/post-comments/index.js +12 -15
  13. package/build/components/post-comments/index.js.map +1 -1
  14. package/build/components/post-excerpt/index.js +9 -20
  15. package/build/components/post-excerpt/index.js.map +1 -1
  16. package/build/components/post-pingbacks/index.js +12 -15
  17. package/build/components/post-pingbacks/index.js.map +1 -1
  18. package/build/components/post-preview-button/index.js +62 -157
  19. package/build/components/post-preview-button/index.js.map +1 -1
  20. package/build/components/post-publish-button/index.js +4 -9
  21. package/build/components/post-publish-button/index.js.map +1 -1
  22. package/build/components/post-publish-button/label.js +2 -4
  23. package/build/components/post-publish-button/label.js.map +1 -1
  24. package/build/components/post-publish-panel/index.js +1 -3
  25. package/build/components/post-publish-panel/index.js.map +1 -1
  26. package/build/components/post-saved-state/index.js +2 -5
  27. package/build/components/post-saved-state/index.js.map +1 -1
  28. package/build/components/post-schedule/label.js +4 -4
  29. package/build/components/post-schedule/label.js.map +1 -1
  30. package/build/components/post-sync-status/index.js +84 -5
  31. package/build/components/post-sync-status/index.js.map +1 -1
  32. package/build/components/post-text-editor/index.js +51 -58
  33. package/build/components/post-text-editor/index.js.map +1 -1
  34. package/build/components/post-type-support-check/index.js +10 -14
  35. package/build/components/post-type-support-check/index.js.map +1 -1
  36. package/build/hooks/custom-sources-backwards-compatibility.js +1 -24
  37. package/build/hooks/custom-sources-backwards-compatibility.js.map +1 -1
  38. package/build/store/actions.js +37 -3
  39. package/build/store/actions.js.map +1 -1
  40. package/build/store/selectors.js +55 -63
  41. package/build/store/selectors.js.map +1 -1
  42. package/build-module/components/entities-saved-states/entity-record-item.js +6 -40
  43. package/build-module/components/entities-saved-states/entity-record-item.js.map +1 -1
  44. package/build-module/components/entities-saved-states/entity-type-list.js +2 -4
  45. package/build-module/components/entities-saved-states/entity-type-list.js.map +1 -1
  46. package/build-module/components/entities-saved-states/index.js +0 -1
  47. package/build-module/components/entities-saved-states/index.js.map +1 -1
  48. package/build-module/components/global-keyboard-shortcuts/{save-shortcut.js → index.js} +12 -23
  49. package/build-module/components/global-keyboard-shortcuts/index.js.map +1 -0
  50. package/build-module/components/index.js +9 -4
  51. package/build-module/components/index.js.map +1 -1
  52. package/build-module/components/post-comments/index.js +13 -14
  53. package/build-module/components/post-comments/index.js.map +1 -1
  54. package/build-module/components/post-excerpt/index.js +10 -19
  55. package/build-module/components/post-excerpt/index.js.map +1 -1
  56. package/build-module/components/post-pingbacks/index.js +13 -14
  57. package/build-module/components/post-pingbacks/index.js.map +1 -1
  58. package/build-module/components/post-preview-button/index.js +63 -149
  59. package/build-module/components/post-preview-button/index.js.map +1 -1
  60. package/build-module/components/post-publish-button/index.js +4 -9
  61. package/build-module/components/post-publish-button/index.js.map +1 -1
  62. package/build-module/components/post-publish-button/label.js +2 -4
  63. package/build-module/components/post-publish-button/label.js.map +1 -1
  64. package/build-module/components/post-publish-panel/index.js +1 -3
  65. package/build-module/components/post-publish-panel/index.js.map +1 -1
  66. package/build-module/components/post-saved-state/index.js +2 -5
  67. package/build-module/components/post-saved-state/index.js.map +1 -1
  68. package/build-module/components/post-schedule/label.js +4 -4
  69. package/build-module/components/post-schedule/label.js.map +1 -1
  70. package/build-module/components/post-sync-status/index.js +84 -8
  71. package/build-module/components/post-sync-status/index.js.map +1 -1
  72. package/build-module/components/post-text-editor/index.js +48 -56
  73. package/build-module/components/post-text-editor/index.js.map +1 -1
  74. package/build-module/components/post-type-support-check/index.js +11 -14
  75. package/build-module/components/post-type-support-check/index.js.map +1 -1
  76. package/build-module/hooks/custom-sources-backwards-compatibility.js +2 -24
  77. package/build-module/hooks/custom-sources-backwards-compatibility.js.map +1 -1
  78. package/build-module/store/actions.js +31 -1
  79. package/build-module/store/actions.js.map +1 -1
  80. package/build-module/store/selectors.js +48 -57
  81. package/build-module/store/selectors.js.map +1 -1
  82. package/build-style/style-rtl.css +0 -18
  83. package/build-style/style.css +0 -18
  84. package/package.json +30 -30
  85. package/src/components/entities-saved-states/entity-record-item.js +3 -61
  86. package/src/components/entities-saved-states/entity-type-list.js +0 -2
  87. package/src/components/entities-saved-states/index.js +0 -1
  88. package/src/components/entities-saved-states/style.scss +0 -15
  89. package/src/components/global-keyboard-shortcuts/index.js +49 -0
  90. package/src/components/index.js +12 -3
  91. package/src/components/post-author/test/check.js +18 -12
  92. package/src/components/post-comments/index.js +11 -17
  93. package/src/components/post-excerpt/index.js +10 -16
  94. package/src/components/post-pingbacks/index.js +11 -15
  95. package/src/components/post-preview-button/index.js +73 -156
  96. package/src/components/post-preview-button/test/index.js +94 -158
  97. package/src/components/post-publish-button/index.js +2 -7
  98. package/src/components/post-publish-button/label.js +2 -2
  99. package/src/components/post-publish-button/test/index.js +0 -10
  100. package/src/components/post-publish-panel/index.js +1 -3
  101. package/src/components/post-saved-state/index.js +2 -5
  102. package/src/components/post-schedule/label.js +4 -4
  103. package/src/components/post-sync-status/index.js +100 -7
  104. package/src/components/post-text-editor/index.js +34 -57
  105. package/src/components/post-title/style.native.scss +5 -5
  106. package/src/components/post-type-support-check/index.js +8 -10
  107. package/src/components/post-type-support-check/test/index.js +35 -19
  108. package/src/hooks/custom-sources-backwards-compatibility.js +1 -25
  109. package/src/store/actions.js +34 -2
  110. package/src/store/selectors.js +47 -43
  111. package/src/store/test/selectors.js +49 -38
  112. package/build/components/global-keyboard-shortcuts/save-shortcut.js.map +0 -1
  113. package/build/components/global-keyboard-shortcuts/text-editor-shortcuts.js +0 -22
  114. package/build/components/global-keyboard-shortcuts/text-editor-shortcuts.js.map +0 -1
  115. package/build/components/global-keyboard-shortcuts/visual-editor-shortcuts.js +0 -45
  116. package/build/components/global-keyboard-shortcuts/visual-editor-shortcuts.js.map +0 -1
  117. package/build-module/components/global-keyboard-shortcuts/save-shortcut.js.map +0 -1
  118. package/build-module/components/global-keyboard-shortcuts/text-editor-shortcuts.js +0 -12
  119. package/build-module/components/global-keyboard-shortcuts/text-editor-shortcuts.js.map +0 -1
  120. package/build-module/components/global-keyboard-shortcuts/visual-editor-shortcuts.js +0 -32
  121. package/build-module/components/global-keyboard-shortcuts/visual-editor-shortcuts.js.map +0 -1
  122. package/src/components/global-keyboard-shortcuts/save-shortcut.js +0 -55
  123. package/src/components/global-keyboard-shortcuts/text-editor-shortcuts.js +0 -8
  124. package/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js +0 -29
  125. package/src/components/post-text-editor/test/index.js +0 -156
@@ -31,7 +31,6 @@ export default function EntityTypeList( {
31
31
  list,
32
32
  unselectedEntities,
33
33
  setUnselectedEntities,
34
- closePanel,
35
34
  } ) {
36
35
  const count = list.length;
37
36
  const firstRecord = list[ 0 ];
@@ -73,7 +72,6 @@ export default function EntityTypeList( {
73
72
  onChange={ ( value ) =>
74
73
  setUnselectedEntities( record, value )
75
74
  }
76
- closePanel={ closePanel }
77
75
  />
78
76
  );
79
77
  } ) }
@@ -210,7 +210,6 @@ export function EntitiesSavedStatesExtensible( {
210
210
  <EntityTypeList
211
211
  key={ list[ 0 ].name }
212
212
  list={ list }
213
- closePanel={ dismissPanel }
214
213
  unselectedEntities={ unselectedEntities }
215
214
  setUnselectedEntities={ setUnselectedEntities }
216
215
  />
@@ -1,18 +1,3 @@
1
- .entities-saved-states__find-entity {
2
- display: none;
3
-
4
- @include break-medium() {
5
- display: block;
6
- }
7
- }
8
- .entities-saved-states__find-entity-small {
9
- display: block;
10
-
11
- @include break-medium() {
12
- display: none;
13
- }
14
- }
15
-
16
1
  .entities-saved-states__panel-header {
17
2
  box-sizing: border-box;
18
3
  background: $white;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useShortcut } from '@wordpress/keyboard-shortcuts';
5
+ import { useDispatch, useSelect } from '@wordpress/data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { store as editorStore } from '../../store';
11
+
12
+ export default function EditorKeyboardShortcuts() {
13
+ const { redo, undo, savePost } = useDispatch( editorStore );
14
+ const { isEditedPostDirty, isPostSavingLocked } = useSelect( editorStore );
15
+
16
+ useShortcut( 'core/editor/undo', ( event ) => {
17
+ undo();
18
+ event.preventDefault();
19
+ } );
20
+
21
+ useShortcut( 'core/editor/redo', ( event ) => {
22
+ redo();
23
+ event.preventDefault();
24
+ } );
25
+
26
+ useShortcut( 'core/editor/save', ( event ) => {
27
+ event.preventDefault();
28
+
29
+ /**
30
+ * Do not save the post if post saving is locked.
31
+ */
32
+ if ( isPostSavingLocked() ) {
33
+ return;
34
+ }
35
+
36
+ // TODO: This should be handled in the `savePost` effect in
37
+ // considering `isSaveable`. See note on `isEditedPostSaveable`
38
+ // selector about dirtiness and meta-boxes.
39
+ //
40
+ // See: `isEditedPostSaveable`
41
+ if ( ! isEditedPostDirty() ) {
42
+ return;
43
+ }
44
+
45
+ savePost();
46
+ } );
47
+
48
+ return null;
49
+ }
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import EditorKeyboardShortcuts from './global-keyboard-shortcuts';
5
+
1
6
  // Block Creation Components.
2
7
  export * from './autocompleters';
3
8
 
@@ -5,8 +10,7 @@ export * from './autocompleters';
5
10
  export { default as AutosaveMonitor } from './autosave-monitor';
6
11
  export { default as DocumentOutline } from './document-outline';
7
12
  export { default as DocumentOutlineCheck } from './document-outline/check';
8
- export { default as VisualEditorGlobalKeyboardShortcuts } from './global-keyboard-shortcuts/visual-editor-shortcuts';
9
- export { default as TextEditorGlobalKeyboardShortcuts } from './global-keyboard-shortcuts/text-editor-shortcuts';
13
+ export { EditorKeyboardShortcuts };
10
14
  export { default as EditorKeyboardShortcutsRegister } from './global-keyboard-shortcuts/register-shortcuts';
11
15
  export { default as EditorHistoryRedo } from './editor-history/redo';
12
16
  export { default as EditorHistoryUndo } from './editor-history/undo';
@@ -51,7 +55,10 @@ export { default as PostSlugCheck } from './post-slug/check';
51
55
  export { default as PostSticky } from './post-sticky';
52
56
  export { default as PostStickyCheck } from './post-sticky/check';
53
57
  export { default as PostSwitchToDraftButton } from './post-switch-to-draft-button';
54
- export { default as PostSyncStatus } from './post-sync-status';
58
+ export {
59
+ default as PostSyncStatus,
60
+ PostSyncStatusModal,
61
+ } from './post-sync-status';
55
62
  export { default as PostTaxonomies } from './post-taxonomies';
56
63
  export { FlatTermSelector as PostTaxonomiesFlatTermSelector } from './post-taxonomies/flat-term-selector';
57
64
  export { HierarchicalTermSelector as PostTaxonomiesHierarchicalTermSelector } from './post-taxonomies/hierarchical-term-selector';
@@ -81,3 +88,5 @@ export { default as CharacterCount } from './character-count';
81
88
  export { default as EditorProvider } from './provider';
82
89
 
83
90
  export * from './deprecated';
91
+ export const VisualEditorGlobalKeyboardShortcuts = EditorKeyboardShortcuts;
92
+ export const TextEditorGlobalKeyboardShortcuts = EditorKeyboardShortcuts;
@@ -19,32 +19,38 @@ jest.mock( '@wordpress/data/src/components/use-select', () => {
19
19
  return mock;
20
20
  } );
21
21
 
22
+ function setupUseSelectMock( hasAssignAuthorAction, hasAuthors ) {
23
+ useSelect.mockImplementation( ( cb ) => {
24
+ return cb( () => ( {
25
+ getPostType: () => ( { supports: { author: true } } ),
26
+ getEditedPostAttribute: () => {},
27
+ getCurrentPost: () => ( {
28
+ _links: {
29
+ 'wp:action-assign-author': hasAssignAuthorAction,
30
+ },
31
+ } ),
32
+ getUsers: () => Array( hasAuthors ? 1 : 0 ).fill( {} ),
33
+ } ) );
34
+ } );
35
+ }
36
+
22
37
  describe( 'PostAuthorCheck', () => {
23
38
  it( 'should not render anything if has no authors', () => {
24
- useSelect.mockImplementation( () => ( {
25
- hasAuthors: false,
26
- hasAssignAuthorAction: true,
27
- } ) );
39
+ setupUseSelectMock( false, true );
28
40
 
29
41
  render( <PostAuthorCheck>authors</PostAuthorCheck> );
30
42
  expect( screen.queryByText( 'authors' ) ).not.toBeInTheDocument();
31
43
  } );
32
44
 
33
45
  it( "should not render anything if doesn't have author action", () => {
34
- useSelect.mockImplementation( () => ( {
35
- hasAuthors: true,
36
- hasAssignAuthorAction: false,
37
- } ) );
46
+ setupUseSelectMock( true, false );
38
47
 
39
48
  render( <PostAuthorCheck>authors</PostAuthorCheck> );
40
49
  expect( screen.queryByText( 'authors' ) ).not.toBeInTheDocument();
41
50
  } );
42
51
 
43
52
  it( 'should render control', () => {
44
- useSelect.mockImplementation( () => ( {
45
- hasAuthors: true,
46
- hasAssignAuthorAction: true,
47
- } ) );
53
+ setupUseSelectMock( true, true );
48
54
 
49
55
  render( <PostAuthorCheck>authors</PostAuthorCheck> );
50
56
  expect( screen.getByText( 'authors' ) ).toBeVisible();
@@ -3,17 +3,23 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { CheckboxControl } from '@wordpress/components';
6
- import { compose } from '@wordpress/compose';
7
- import { withSelect, withDispatch } from '@wordpress/data';
6
+ import { useDispatch, useSelect } from '@wordpress/data';
8
7
 
9
8
  /**
10
9
  * Internal dependencies
11
10
  */
12
11
  import { store as editorStore } from '../../store';
13
12
 
14
- function PostComments( { commentStatus = 'open', ...props } ) {
13
+ function PostComments() {
14
+ const commentStatus = useSelect(
15
+ ( select ) =>
16
+ select( editorStore ).getEditedPostAttribute( 'comment_status' ) ??
17
+ 'open',
18
+ []
19
+ );
20
+ const { editPost } = useDispatch( editorStore );
15
21
  const onToggleComments = () =>
16
- props.editPost( {
22
+ editPost( {
17
23
  comment_status: commentStatus === 'open' ? 'closed' : 'open',
18
24
  } );
19
25
 
@@ -27,16 +33,4 @@ function PostComments( { commentStatus = 'open', ...props } ) {
27
33
  );
28
34
  }
29
35
 
30
- export default compose( [
31
- withSelect( ( select ) => {
32
- return {
33
- commentStatus:
34
- select( editorStore ).getEditedPostAttribute(
35
- 'comment_status'
36
- ),
37
- };
38
- } ),
39
- withDispatch( ( dispatch ) => ( {
40
- editPost: dispatch( editorStore ).editPost,
41
- } ) ),
42
- ] )( PostComments );
36
+ export default PostComments;
@@ -3,22 +3,27 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { ExternalLink, TextareaControl } from '@wordpress/components';
6
- import { withSelect, withDispatch } from '@wordpress/data';
7
- import { compose } from '@wordpress/compose';
6
+ import { useDispatch, useSelect } from '@wordpress/data';
8
7
 
9
8
  /**
10
9
  * Internal dependencies
11
10
  */
12
11
  import { store as editorStore } from '../../store';
13
12
 
14
- function PostExcerpt( { excerpt, onUpdateExcerpt } ) {
13
+ function PostExcerpt() {
14
+ const excerpt = useSelect(
15
+ ( select ) => select( editorStore ).getEditedPostAttribute( 'excerpt' ),
16
+ []
17
+ );
18
+ const { editPost } = useDispatch( editorStore );
19
+
15
20
  return (
16
21
  <div className="editor-post-excerpt">
17
22
  <TextareaControl
18
23
  __nextHasNoMarginBottom
19
24
  label={ __( 'Write an excerpt (optional)' ) }
20
25
  className="editor-post-excerpt__textarea"
21
- onChange={ ( value ) => onUpdateExcerpt( value ) }
26
+ onChange={ ( value ) => editPost( { excerpt: value } ) }
22
27
  value={ excerpt }
23
28
  />
24
29
  <ExternalLink
@@ -32,15 +37,4 @@ function PostExcerpt( { excerpt, onUpdateExcerpt } ) {
32
37
  );
33
38
  }
34
39
 
35
- export default compose( [
36
- withSelect( ( select ) => {
37
- return {
38
- excerpt: select( editorStore ).getEditedPostAttribute( 'excerpt' ),
39
- };
40
- } ),
41
- withDispatch( ( dispatch ) => ( {
42
- onUpdateExcerpt( excerpt ) {
43
- dispatch( editorStore ).editPost( { excerpt } );
44
- },
45
- } ) ),
46
- ] )( PostExcerpt );
40
+ export default PostExcerpt;
@@ -3,17 +3,23 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { CheckboxControl } from '@wordpress/components';
6
- import { withSelect, withDispatch } from '@wordpress/data';
7
- import { compose } from '@wordpress/compose';
6
+ import { useDispatch, useSelect } from '@wordpress/data';
8
7
 
9
8
  /**
10
9
  * Internal dependencies
11
10
  */
12
11
  import { store as editorStore } from '../../store';
13
12
 
14
- function PostPingbacks( { pingStatus = 'open', ...props } ) {
13
+ function PostPingbacks() {
14
+ const pingStatus = useSelect(
15
+ ( select ) =>
16
+ select( editorStore ).getEditedPostAttribute( 'ping_status' ) ??
17
+ 'open',
18
+ []
19
+ );
20
+ const { editPost } = useDispatch( editorStore );
15
21
  const onTogglePingback = () =>
16
- props.editPost( {
22
+ editPost( {
17
23
  ping_status: pingStatus === 'open' ? 'closed' : 'open',
18
24
  } );
19
25
 
@@ -27,14 +33,4 @@ function PostPingbacks( { pingStatus = 'open', ...props } ) {
27
33
  );
28
34
  }
29
35
 
30
- export default compose( [
31
- withSelect( ( select ) => {
32
- return {
33
- pingStatus:
34
- select( editorStore ).getEditedPostAttribute( 'ping_status' ),
35
- };
36
- } ),
37
- withDispatch( ( dispatch ) => ( {
38
- editPost: dispatch( editorStore ).editPost,
39
- } ) ),
40
- ] )( PostPingbacks );
36
+ export default PostPingbacks;
@@ -1,16 +1,10 @@
1
- /**
2
- * External dependencies
3
- */
4
- import classnames from 'classnames';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
9
- import { Component, createRef, renderToString } from '@wordpress/element';
4
+ import { renderToString } from '@wordpress/element';
10
5
  import { Button, Path, SVG, VisuallyHidden } from '@wordpress/components';
11
6
  import { __, _x } from '@wordpress/i18n';
12
- import { withSelect, withDispatch } from '@wordpress/data';
13
- import { ifCondition, compose } from '@wordpress/compose';
7
+ import { useSelect, useDispatch } from '@wordpress/data';
14
8
  import { applyFilters } from '@wordpress/hooks';
15
9
  import { store as coreStore } from '@wordpress/core-data';
16
10
 
@@ -105,48 +99,40 @@ function writeInterstitialMessage( targetDocument ) {
105
99
  targetDocument.close();
106
100
  }
107
101
 
108
- export class PostPreviewButton extends Component {
109
- constructor() {
110
- super( ...arguments );
111
-
112
- this.buttonRef = createRef();
113
-
114
- this.openPreviewWindow = this.openPreviewWindow.bind( this );
115
- }
116
-
117
- componentDidUpdate( prevProps ) {
118
- const { previewLink } = this.props;
119
- // This relies on the window being responsible to unset itself when
120
- // navigation occurs or a new preview window is opened, to avoid
121
- // unintentional forceful redirects.
122
- if ( previewLink && ! prevProps.previewLink ) {
123
- this.setPreviewWindowLink( previewLink );
124
- }
125
- }
126
-
127
- /**
128
- * Sets the preview window's location to the given URL, if a preview window
129
- * exists and is not closed.
130
- *
131
- * @param {string} url URL to assign as preview window location.
132
- */
133
- setPreviewWindowLink( url ) {
134
- const { previewWindow } = this;
135
-
136
- if ( previewWindow && ! previewWindow.closed ) {
137
- previewWindow.location = url;
138
- if ( this.buttonRef.current ) {
139
- this.buttonRef.current.focus();
140
- }
141
- }
102
+ export default function PostPreviewButton( {
103
+ className,
104
+ textContent,
105
+ forceIsAutosaveable,
106
+ role,
107
+ onPreview,
108
+ } ) {
109
+ const { postId, currentPostLink, previewLink, isSaveable, isViewable } =
110
+ useSelect( ( select ) => {
111
+ const editor = select( editorStore );
112
+ const core = select( coreStore );
113
+
114
+ const postType = core.getPostType(
115
+ editor.getCurrentPostType( 'type' )
116
+ );
117
+
118
+ return {
119
+ postId: editor.getCurrentPostId(),
120
+ currentPostLink: editor.getCurrentPostAttribute( 'link' ),
121
+ previewLink: editor.getEditedPostPreviewLink(),
122
+ isSaveable: editor.isEditedPostSaveable(),
123
+ isViewable: postType?.viewable ?? false,
124
+ };
125
+ }, [] );
126
+
127
+ const { __unstableSaveForPreview } = useDispatch( editorStore );
128
+
129
+ if ( ! isViewable ) {
130
+ return null;
142
131
  }
143
132
 
144
- getWindowTarget() {
145
- const { postId } = this.props;
146
- return `wp-preview-${ postId }`;
147
- }
133
+ const targetId = `wp-preview-${ postId }`;
148
134
 
149
- openPreviewWindow( event ) {
135
+ const openPreviewWindow = async ( event ) => {
150
136
  // Our Preview button has its 'href' and 'target' set correctly for a11y
151
137
  // purposes. Unfortunately, though, we can't rely on the default 'click'
152
138
  // handler since sometimes it incorrectly opens a new tab instead of reusing
@@ -155,117 +141,48 @@ export class PostPreviewButton extends Component {
155
141
  event.preventDefault();
156
142
 
157
143
  // Open up a Preview tab if needed. This is where we'll show the preview.
158
- if ( ! this.previewWindow || this.previewWindow.closed ) {
159
- this.previewWindow = window.open( '', this.getWindowTarget() );
160
- }
144
+ const previewWindow = window.open( '', targetId );
161
145
 
162
146
  // Focus the Preview tab. This might not do anything, depending on the browser's
163
147
  // and user's preferences.
164
148
  // https://html.spec.whatwg.org/multipage/interaction.html#dom-window-focus
165
- this.previewWindow.focus();
166
-
167
- if (
168
- // If we don't need to autosave the post before previewing, then we simply
169
- // load the Preview URL in the Preview tab.
170
- ! this.props.isAutosaveable ||
171
- // Do not save or overwrite the post, if the post is already locked.
172
- this.props.isPostLocked
173
- ) {
174
- this.setPreviewWindowLink( event.target.href );
175
- return;
176
- }
177
-
178
- // Request an autosave. This happens asynchronously and causes the component
179
- // to update when finished.
180
- if ( this.props.isDraft ) {
181
- this.props.savePost( { isPreview: true } );
182
- } else {
183
- this.props.autosave( { isPreview: true } );
184
- }
185
-
186
- // Display a 'Generating preview' message in the Preview tab while we wait for the
187
- // autosave to finish.
188
- writeInterstitialMessage( this.previewWindow.document );
189
- }
190
-
191
- render() {
192
- const { previewLink, currentPostLink, isSaveable, role } = this.props;
193
-
194
- // Link to the `?preview=true` URL if we have it, since this lets us see
195
- // changes that were autosaved since the post was last published. Otherwise,
196
- // just link to the post's URL.
197
- const href = previewLink || currentPostLink;
198
-
199
- const classNames = classnames(
200
- {
201
- 'editor-post-preview': ! this.props.className,
202
- },
203
- this.props.className
204
- );
205
-
206
- return (
207
- <Button
208
- variant={ ! this.props.className ? 'tertiary' : undefined }
209
- className={ classNames }
210
- href={ href }
211
- target={ this.getWindowTarget() }
212
- disabled={ ! isSaveable }
213
- onClick={ this.openPreviewWindow }
214
- ref={ this.buttonRef }
215
- role={ role }
216
- >
217
- { this.props.textContent ? (
218
- this.props.textContent
219
- ) : (
220
- <>
221
- { _x( 'Preview', 'imperative verb' ) }
222
- <VisuallyHidden as="span">
223
- {
224
- /* translators: accessibility text */
225
- __( '(opens in a new tab)' )
226
- }
227
- </VisuallyHidden>
228
- </>
229
- ) }
230
- </Button>
231
- );
232
- }
149
+ previewWindow.focus();
150
+
151
+ writeInterstitialMessage( previewWindow.document );
152
+
153
+ const link = await __unstableSaveForPreview( { forceIsAutosaveable } );
154
+
155
+ previewWindow.location = link;
156
+
157
+ onPreview?.();
158
+ };
159
+
160
+ // Link to the `?preview=true` URL if we have it, since this lets us see
161
+ // changes that were autosaved since the post was last published. Otherwise,
162
+ // just link to the post's URL.
163
+ const href = previewLink || currentPostLink;
164
+
165
+ return (
166
+ <Button
167
+ variant={ ! className ? 'tertiary' : undefined }
168
+ className={ className || 'editor-post-preview' }
169
+ href={ href }
170
+ target={ targetId }
171
+ disabled={ ! isSaveable }
172
+ onClick={ openPreviewWindow }
173
+ role={ role }
174
+ >
175
+ { textContent || (
176
+ <>
177
+ { _x( 'Preview', 'imperative verb' ) }
178
+ <VisuallyHidden as="span">
179
+ {
180
+ /* translators: accessibility text */
181
+ __( '(opens in a new tab)' )
182
+ }
183
+ </VisuallyHidden>
184
+ </>
185
+ ) }
186
+ </Button>
187
+ );
233
188
  }
234
-
235
- export default compose( [
236
- withSelect( ( select, { forcePreviewLink, forceIsAutosaveable } ) => {
237
- const {
238
- getCurrentPostId,
239
- getCurrentPostAttribute,
240
- getEditedPostAttribute,
241
- isEditedPostSaveable,
242
- isEditedPostAutosaveable,
243
- getEditedPostPreviewLink,
244
- isPostLocked,
245
- } = select( editorStore );
246
- const { getPostType } = select( coreStore );
247
-
248
- const previewLink = getEditedPostPreviewLink();
249
- const postType = getPostType( getEditedPostAttribute( 'type' ) );
250
-
251
- return {
252
- postId: getCurrentPostId(),
253
- currentPostLink: getCurrentPostAttribute( 'link' ),
254
- previewLink:
255
- forcePreviewLink !== undefined ? forcePreviewLink : previewLink,
256
- isSaveable: isEditedPostSaveable(),
257
- isAutosaveable: forceIsAutosaveable || isEditedPostAutosaveable(),
258
- isViewable: postType?.viewable ?? false,
259
- isDraft:
260
- [ 'draft', 'auto-draft' ].indexOf(
261
- getEditedPostAttribute( 'status' )
262
- ) !== -1,
263
- isPostLocked: isPostLocked(),
264
- };
265
- } ),
266
- withDispatch( ( dispatch ) => ( {
267
- autosave: dispatch( editorStore ).autosave,
268
- savePost: dispatch( editorStore ).savePost,
269
- } ) ),
270
- ifCondition( ( { isViewable } ) => isViewable ),
271
- ] )( PostPreviewButton );