@wordpress/editor 14.48.2-next.v.202606191442.0 → 14.49.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 (95) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +5 -13
  3. package/build/components/autosave-monitor/index.cjs +40 -83
  4. package/build/components/autosave-monitor/index.cjs.map +3 -3
  5. package/build/components/post-publish-panel/postpublish.cjs +0 -1
  6. package/build/components/post-publish-panel/postpublish.cjs.map +2 -2
  7. package/build/components/post-status/index.cjs +0 -1
  8. package/build/components/post-status/index.cjs.map +2 -2
  9. package/build/components/post-taxonomies/hierarchical-term-selector.cjs +0 -1
  10. package/build/components/post-taxonomies/hierarchical-term-selector.cjs.map +2 -2
  11. package/build/components/post-template/create-new-template-modal.cjs +0 -1
  12. package/build/components/post-template/create-new-template-modal.cjs.map +2 -2
  13. package/build/components/post-text-editor/utils.cjs +2 -2
  14. package/build/components/post-text-editor/utils.cjs.map +1 -1
  15. package/build/components/post-visibility/index.cjs +0 -1
  16. package/build/components/post-visibility/index.cjs.map +2 -2
  17. package/build/components/provider/disable-non-page-content-blocks.cjs +23 -3
  18. package/build/components/provider/disable-non-page-content-blocks.cjs.map +2 -2
  19. package/build/components/provider/navigation-block-editing-mode.cjs +9 -2
  20. package/build/components/provider/navigation-block-editing-mode.cjs.map +2 -2
  21. package/build/components/provider/use-block-editor-settings.cjs +2 -1
  22. package/build/components/provider/use-block-editor-settings.cjs.map +2 -2
  23. package/build/components/sidebar/dataform-post-summary.cjs +87 -32
  24. package/build/components/sidebar/dataform-post-summary.cjs.map +3 -3
  25. package/build/components/sidebar/post-summary.cjs +8 -6
  26. package/build/components/sidebar/post-summary.cjs.map +3 -3
  27. package/build/dataviews/store/private-actions.cjs +3 -1
  28. package/build/dataviews/store/private-actions.cjs.map +2 -2
  29. package/build/store/private-actions.cjs +2 -2
  30. package/build/store/private-actions.cjs.map +2 -2
  31. package/build-module/components/autosave-monitor/index.mjs +42 -81
  32. package/build-module/components/autosave-monitor/index.mjs.map +2 -2
  33. package/build-module/components/post-publish-panel/postpublish.mjs +0 -1
  34. package/build-module/components/post-publish-panel/postpublish.mjs.map +2 -2
  35. package/build-module/components/post-status/index.mjs +0 -1
  36. package/build-module/components/post-status/index.mjs.map +2 -2
  37. package/build-module/components/post-taxonomies/hierarchical-term-selector.mjs +0 -1
  38. package/build-module/components/post-taxonomies/hierarchical-term-selector.mjs.map +2 -2
  39. package/build-module/components/post-template/create-new-template-modal.mjs +0 -1
  40. package/build-module/components/post-template/create-new-template-modal.mjs.map +2 -2
  41. package/build-module/components/post-text-editor/utils.mjs +2 -2
  42. package/build-module/components/post-text-editor/utils.mjs.map +1 -1
  43. package/build-module/components/post-visibility/index.mjs +0 -1
  44. package/build-module/components/post-visibility/index.mjs.map +2 -2
  45. package/build-module/components/provider/disable-non-page-content-blocks.mjs +23 -3
  46. package/build-module/components/provider/disable-non-page-content-blocks.mjs.map +2 -2
  47. package/build-module/components/provider/navigation-block-editing-mode.mjs +10 -3
  48. package/build-module/components/provider/navigation-block-editing-mode.mjs.map +2 -2
  49. package/build-module/components/provider/use-block-editor-settings.mjs +2 -1
  50. package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
  51. package/build-module/components/sidebar/dataform-post-summary.mjs +88 -33
  52. package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
  53. package/build-module/components/sidebar/post-summary.mjs +8 -6
  54. package/build-module/components/sidebar/post-summary.mjs.map +2 -2
  55. package/build-module/dataviews/store/private-actions.mjs +5 -1
  56. package/build-module/dataviews/store/private-actions.mjs.map +2 -2
  57. package/build-module/store/private-actions.mjs +2 -2
  58. package/build-module/store/private-actions.mjs.map +2 -2
  59. package/build-style/style-rtl.css +29 -33
  60. package/build-style/style.css +29 -33
  61. package/build-types/components/autosave-monitor/index.d.ts +11 -29
  62. package/build-types/components/autosave-monitor/index.d.ts.map +1 -1
  63. package/build-types/components/post-publish-panel/postpublish.d.ts.map +1 -1
  64. package/build-types/components/post-status/index.d.ts.map +1 -1
  65. package/build-types/components/post-taxonomies/hierarchical-term-selector.d.ts.map +1 -1
  66. package/build-types/components/post-template/create-new-template-modal.d.ts.map +1 -1
  67. package/build-types/components/post-visibility/index.d.ts.map +1 -1
  68. package/build-types/components/provider/disable-non-page-content-blocks.d.ts.map +1 -1
  69. package/build-types/components/provider/navigation-block-editing-mode.d.ts.map +1 -1
  70. package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
  71. package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -1
  72. package/build-types/components/sidebar/post-summary.d.ts.map +1 -1
  73. package/build-types/dataviews/store/private-actions.d.ts.map +1 -1
  74. package/build-types/store/selectors.d.ts.map +1 -1
  75. package/build-types/utils/get-template-part-icon.d.ts.map +1 -1
  76. package/package.json +46 -46
  77. package/src/components/autosave-monitor/index.js +69 -104
  78. package/src/components/autosave-monitor/test/index.js +137 -122
  79. package/src/components/plugin-post-publish-panel/test/__snapshots__/index.js.snap +1 -0
  80. package/src/components/post-publish-panel/postpublish.js +0 -1
  81. package/src/components/post-publish-panel/test/__snapshots__/index.js.snap +7 -2
  82. package/src/components/post-saved-state/test/__snapshots__/index.js.snap +1 -0
  83. package/src/components/post-status/index.js +0 -1
  84. package/src/components/post-taxonomies/hierarchical-term-selector.js +0 -1
  85. package/src/components/post-template/create-new-template-modal.js +0 -1
  86. package/src/components/post-text-editor/utils.js +2 -2
  87. package/src/components/post-visibility/index.js +0 -1
  88. package/src/components/provider/disable-non-page-content-blocks.js +23 -6
  89. package/src/components/provider/navigation-block-editing-mode.js +10 -4
  90. package/src/components/provider/use-block-editor-settings.js +3 -1
  91. package/src/components/sidebar/dataform-post-summary.js +133 -49
  92. package/src/components/sidebar/post-summary.js +12 -10
  93. package/src/dataviews/store/private-actions.ts +8 -1
  94. package/src/store/private-actions.js +2 -2
  95. package/src/store/test/actions.js +2 -2
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { Component } from '@wordpress/element';
5
- import { compose } from '@wordpress/compose';
6
- import { withSelect, withDispatch } from '@wordpress/data';
4
+ import { useEffect, useRef } from '@wordpress/element';
5
+ import { useSelect, useDispatch } from '@wordpress/data';
7
6
  import { store as coreStore } from '@wordpress/core-data';
8
7
 
9
8
  /**
@@ -11,123 +10,89 @@ import { store as coreStore } from '@wordpress/core-data';
11
10
  */
12
11
  import { store as editorStore } from '../../store';
13
12
 
14
- export class AutosaveMonitor extends Component {
15
- constructor( props ) {
16
- super( props );
17
- this.needsAutosave = !! ( props.isDirty && props.isAutosaveable );
18
- }
19
-
20
- componentDidMount() {
21
- if ( ! this.props.disableIntervalChecks ) {
22
- this.setAutosaveTimer();
23
- }
24
- }
25
-
26
- componentDidUpdate( prevProps ) {
27
- if ( this.props.disableIntervalChecks ) {
28
- if ( this.props.editsReference !== prevProps.editsReference ) {
29
- this.props.autosave();
30
- }
31
- return;
32
- }
33
-
34
- if ( this.props.interval !== prevProps.interval ) {
35
- clearTimeout( this.timerId );
36
- this.setAutosaveTimer();
37
- }
38
-
39
- if ( ! this.props.isDirty ) {
40
- this.needsAutosave = false;
41
- return;
42
- }
43
-
44
- if ( this.props.isAutosaving && ! prevProps.isAutosaving ) {
45
- this.needsAutosave = false;
46
- return;
47
- }
48
-
49
- if ( this.props.editsReference !== prevProps.editsReference ) {
50
- this.needsAutosave = true;
51
- }
52
- }
53
-
54
- componentWillUnmount() {
55
- clearTimeout( this.timerId );
56
- }
13
+ /**
14
+ * Calls `callback` every `intervalInSeconds`. The latest `callback` is always
15
+ * invoked without resetting the timer.
16
+ *
17
+ * @param {Function} callback Function to call on each tick.
18
+ * @param {number} intervalInSeconds Seconds between ticks.
19
+ */
20
+ function useInterval( callback, intervalInSeconds ) {
21
+ const callbackRef = useRef( callback );
57
22
 
58
- setAutosaveTimer( timeout = this.props.interval * 1000 ) {
59
- this.timerId = setTimeout( () => {
60
- this.autosaveTimerHandler();
61
- }, timeout );
62
- }
23
+ useEffect( () => {
24
+ callbackRef.current = callback;
25
+ }, [ callback ] );
63
26
 
64
- autosaveTimerHandler() {
65
- if ( ! this.props.isAutosaveable ) {
66
- this.setAutosaveTimer( 1000 );
27
+ useEffect( () => {
28
+ // Interval can be undefined before editor settings are populated.
29
+ if ( ! intervalInSeconds ) {
67
30
  return;
68
31
  }
69
32
 
70
- if ( this.needsAutosave ) {
71
- this.needsAutosave = false;
72
- this.props.autosave();
73
- }
74
-
75
- this.setAutosaveTimer();
76
- }
77
-
78
- render() {
79
- return null;
80
- }
33
+ const id = setInterval(
34
+ () => callbackRef.current(),
35
+ intervalInSeconds * 1000
36
+ );
37
+ return () => clearInterval( id );
38
+ }, [ intervalInSeconds ] );
81
39
  }
82
40
 
83
41
  /**
84
42
  * Monitors the changes made to the edited post and triggers autosave if necessary.
85
43
  *
86
- * The logic is straightforward: a check is performed every `props.interval` seconds. If any changes are detected, `props.autosave()` is called.
87
- * The time between the change and the autosave varies but is no larger than `props.interval` seconds. Refer to the code below for more details, such as
88
- * the specific way of detecting changes.
44
+ * The post is checked every `interval` seconds and autosaved when there is something new to save.
89
45
  *
90
- * There are two caveats:
91
- * * If `props.isAutosaveable` happens to be false at a time of checking for changes, the check is retried every second.
92
- * * The timer may be disabled by setting `props.disableIntervalChecks` to `true`. In that mode, any change will immediately trigger `props.autosave()`.
93
- *
94
- * @param {Object} props - The properties passed to the component.
95
- * @param {Function} props.autosave - The function to call when changes need to be saved.
96
- * @param {number} props.interval - The maximum time in seconds between an unsaved change and an autosave.
97
- * @param {boolean} props.isAutosaveable - If false, the check for changes is retried every second.
98
- * @param {boolean} props.disableIntervalChecks - If true, disables the timer and any change will immediately trigger `props.autosave()`.
99
- * @param {boolean} props.isDirty - Indicates if there are unsaved changes.
46
+ * @param {Object} props The component props.
47
+ * @param {number} [props.interval] Time in seconds between checks. Defaults to the editor's
48
+ * `autosaveInterval` setting.
49
+ * @param {Function} [props.autosave] Function to call when changes need to be saved. Defaults to the
50
+ * editor store's `autosave` action.
100
51
  *
101
52
  * @example
102
53
  * ```jsx
103
- * <AutosaveMonitor interval={30000} />
54
+ * <AutosaveMonitor interval={ 30 } />
104
55
  * ```
105
56
  */
106
- export default compose( [
107
- withSelect( ( select, ownProps ) => {
108
- const { getReferenceByDistinctEdits } = select( coreStore );
57
+ export default function AutosaveMonitor( { interval, autosave } ) {
58
+ const { autosave: autosaveAction } = useDispatch( editorStore );
59
+ const triggerAutosave = autosave ?? autosaveAction;
60
+
61
+ const { getReferenceByDistinctEdits } = useSelect( coreStore );
62
+ const { isEditedPostDirty, isEditedPostAutosaveable, isAutosavingPost } =
63
+ useSelect( editorStore );
64
+
65
+ const autosaveInterval = useSelect(
66
+ ( select ) => {
67
+ if ( interval !== undefined ) {
68
+ return interval;
69
+ }
109
70
 
110
- const {
111
- isEditedPostDirty,
112
- isEditedPostAutosaveable,
113
- isAutosavingPost,
114
- getEditorSettings,
115
- } = select( editorStore );
71
+ return select( editorStore ).getEditorSettings().autosaveInterval;
72
+ },
73
+ [ interval ]
74
+ );
116
75
 
117
- const { interval = getEditorSettings().autosaveInterval } = ownProps;
76
+ // Reference of the edits last considered for autosaving. Mutable state that
77
+ // must not trigger a re-render, hence a ref.
78
+ const lastEditsReferenceRef = useRef();
118
79
 
119
- return {
120
- editsReference: getReferenceByDistinctEdits(),
121
- isDirty: isEditedPostDirty(),
122
- isAutosaveable: isEditedPostAutosaveable(),
123
- isAutosaving: isAutosavingPost(),
124
- interval,
125
- };
126
- } ),
127
- withDispatch( ( dispatch, ownProps ) => ( {
128
- autosave() {
129
- const { autosave = dispatch( editorStore ).autosave } = ownProps;
130
- autosave();
131
- },
132
- } ) ),
133
- ] )( AutosaveMonitor );
80
+ useInterval( () => {
81
+ // The post can't be autosaved yet (e.g. its existing autosave is still
82
+ // loading). Keep any pending edits and try again on the next tick.
83
+ if ( ! isEditedPostAutosaveable() ) {
84
+ return;
85
+ }
86
+
87
+ const editsReference = getReferenceByDistinctEdits();
88
+ const hasNewEdits = editsReference !== lastEditsReferenceRef.current;
89
+ if ( hasNewEdits && isEditedPostDirty() && ! isAutosavingPost() ) {
90
+ // Only consume the edits reference when we autosave,
91
+ // so edits made during an in-flight autosave aren't skipped.
92
+ lastEditsReferenceRef.current = editsReference;
93
+ triggerAutosave();
94
+ }
95
+ }, autosaveInterval );
96
+
97
+ return null;
98
+ }
@@ -3,195 +3,210 @@
3
3
  */
4
4
  import { render } from '@testing-library/react';
5
5
 
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useSelect, useDispatch } from '@wordpress/data';
10
+
6
11
  /**
7
12
  * Internal dependencies
8
13
  */
9
- import { AutosaveMonitor } from '../';
14
+ import AutosaveMonitor from '../';
15
+
16
+ jest.mock( '@wordpress/data/src/components/use-select', () => jest.fn() );
17
+ jest.mock( '@wordpress/data/src/components/use-dispatch', () => ( {
18
+ useDispatch: jest.fn(),
19
+ } ) );
10
20
 
11
21
  jest.useFakeTimers();
12
- jest.spyOn( global, 'clearTimeout' );
13
- jest.spyOn( global, 'setTimeout' );
22
+ jest.spyOn( global, 'clearInterval' );
23
+ jest.spyOn( global, 'setInterval' );
24
+
25
+ // The current edited-post state, read live by the mocked selectors so it can be
26
+ // changed between timer ticks without re-rendering the component.
27
+ let state;
28
+
29
+ function setState( overrides = {} ) {
30
+ state = {
31
+ editsReference: 1,
32
+ isDirty: false,
33
+ isAutosaveable: false,
34
+ isAutosaving: false,
35
+ autosaveInterval: 10,
36
+ ...overrides,
37
+ };
38
+ }
14
39
 
15
40
  describe( 'AutosaveMonitor', () => {
16
- let setAutosaveTimerSpy;
17
41
  beforeEach( () => {
18
- setAutosaveTimerSpy = jest.spyOn(
19
- AutosaveMonitor.prototype,
20
- 'setAutosaveTimer'
21
- );
22
- } );
23
-
24
- afterEach( () => {
25
- setAutosaveTimerSpy.mockClear();
42
+ setState();
43
+ // `useSelect( store )` (static mode) returns bound selectors; the
44
+ // component destructures the ones it needs from each call. The mapping
45
+ // form `useSelect( mapSelect )` receives a `select` that resolves to the
46
+ // same selectors.
47
+ useSelect.mockImplementation( ( mapSelectOrStore ) => {
48
+ const selectors = {
49
+ getReferenceByDistinctEdits: () => state.editsReference,
50
+ isEditedPostDirty: () => state.isDirty,
51
+ isEditedPostAutosaveable: () => state.isAutosaveable,
52
+ isAutosavingPost: () => state.isAutosaving,
53
+ getEditorSettings: () => ( {
54
+ autosaveInterval: state.autosaveInterval,
55
+ } ),
56
+ };
57
+ if ( typeof mapSelectOrStore === 'function' ) {
58
+ return mapSelectOrStore( () => selectors );
59
+ }
60
+ return selectors;
61
+ } );
62
+ useDispatch.mockReturnValue( { autosave: jest.fn() } );
63
+ setInterval.mockClear();
64
+ clearInterval.mockClear();
26
65
  } );
27
66
 
28
67
  it( 'should render nothing', () => {
29
- const { container } = render( <AutosaveMonitor isDirty /> );
68
+ const { container } = render( <AutosaveMonitor /> );
30
69
 
31
70
  expect( container ).toBeEmptyDOMElement();
32
71
  } );
33
72
 
34
- it( 'should start autosave timer after being mounted', () => {
35
- render( <AutosaveMonitor isDirty /> );
73
+ it( 'should start the autosave timer after being mounted', () => {
74
+ render( <AutosaveMonitor /> );
36
75
 
37
- expect( setAutosaveTimerSpy ).toHaveBeenCalled();
76
+ expect( setInterval ).toHaveBeenCalled();
38
77
  } );
39
78
 
40
79
  it( 'should clear the autosave timer after being unmounted', () => {
41
- const { rerender } = render( <AutosaveMonitor isDirty /> );
80
+ const { rerender } = render( <AutosaveMonitor /> );
42
81
 
43
82
  rerender( <div /> );
44
83
 
45
- expect( clearTimeout ).toHaveBeenCalled();
84
+ expect( clearInterval ).toHaveBeenCalled();
46
85
  } );
47
86
 
48
- it( 'should clear and restart autosave timer when the interval changes', () => {
49
- const { rerender } = render( <AutosaveMonitor isDirty /> );
87
+ it( 'should restart the autosave timer when the interval changes', () => {
88
+ const { rerender } = render( <AutosaveMonitor interval={ 10 } /> );
50
89
 
51
- rerender( <AutosaveMonitor isDirty interval={ 999 } /> );
90
+ setInterval.mockClear();
91
+ clearInterval.mockClear();
92
+ rerender( <AutosaveMonitor interval={ 999 } /> );
52
93
 
53
- expect( clearTimeout ).toHaveBeenCalled();
54
- expect( setAutosaveTimerSpy ).toHaveBeenCalledTimes( 2 );
94
+ expect( clearInterval ).toHaveBeenCalled();
95
+ expect( setInterval ).toHaveBeenCalledTimes( 1 );
55
96
  } );
56
97
 
57
- it( 'should autosave when `editReference` changes', () => {
58
- const autosave = jest.fn();
59
- const { rerender } = render(
60
- <AutosaveMonitor isDirty isAutosaveable autosave={ autosave } />
61
- );
62
-
63
- expect( autosave ).not.toHaveBeenCalled();
98
+ it( 'should schedule the timer using the interval in seconds', () => {
99
+ render( <AutosaveMonitor interval={ 5 } /> );
64
100
 
65
- rerender(
66
- <AutosaveMonitor
67
- isDirty
68
- isAutosaveable
69
- autosave={ autosave }
70
- editsReference={ [] }
71
- />
101
+ expect( setInterval ).toHaveBeenLastCalledWith(
102
+ expect.any( Function ),
103
+ 5000
72
104
  );
105
+ } );
73
106
 
74
- jest.runOnlyPendingTimers();
107
+ it( 'should fall back to the editor autosaveInterval setting', () => {
108
+ setState( { autosaveInterval: 7 } );
109
+ render( <AutosaveMonitor /> );
75
110
 
76
- expect( autosave ).toHaveBeenCalledTimes( 1 );
111
+ expect( setInterval ).toHaveBeenLastCalledWith(
112
+ expect.any( Function ),
113
+ 7000
114
+ );
77
115
  } );
78
116
 
79
- it( 'should autosave when `editReference` changes and the post becomes dirty', () => {
117
+ it( 'should autosave a dirty, autosaveable post on the timer tick', () => {
80
118
  const autosave = jest.fn();
81
- const { rerender } = render(
82
- <AutosaveMonitor isAutosaveable autosave={ autosave } />
83
- );
119
+ setState( { isDirty: true, isAutosaveable: true } );
120
+ render( <AutosaveMonitor autosave={ autosave } interval={ 5 } /> );
84
121
 
85
122
  expect( autosave ).not.toHaveBeenCalled();
86
123
 
87
- rerender(
88
- <AutosaveMonitor
89
- isDirty
90
- isAutosaveable
91
- autosave={ autosave }
92
- editsReference={ [] }
93
- />
94
- );
95
-
96
- jest.runOnlyPendingTimers();
124
+ jest.advanceTimersByTime( 5000 );
97
125
 
98
126
  expect( autosave ).toHaveBeenCalledTimes( 1 );
99
127
  } );
100
128
 
101
- it( 'should not autosave when `editReference` changes and the post is not dirty anymore', () => {
129
+ it( 'should not autosave when the post is not dirty', () => {
102
130
  const autosave = jest.fn();
103
- const { rerender } = render(
104
- <AutosaveMonitor isDirty isAutosaveable autosave={ autosave } />
105
- );
131
+ setState( { isDirty: false, isAutosaveable: true } );
132
+ render( <AutosaveMonitor autosave={ autosave } interval={ 5 } /> );
133
+
134
+ jest.advanceTimersByTime( 5000 );
106
135
 
107
136
  expect( autosave ).not.toHaveBeenCalled();
137
+ } );
108
138
 
109
- rerender(
110
- <AutosaveMonitor
111
- isAutosaveable
112
- autosave={ autosave }
113
- editsReference={ [] }
114
- />
115
- );
139
+ it( 'should not autosave while an autosave is already in progress', () => {
140
+ const autosave = jest.fn();
141
+ setState( { isDirty: true, isAutosaveable: true, isAutosaving: true } );
142
+ render( <AutosaveMonitor autosave={ autosave } interval={ 5 } /> );
116
143
 
117
- jest.runOnlyPendingTimers();
144
+ jest.advanceTimersByTime( 5000 );
118
145
 
119
146
  expect( autosave ).not.toHaveBeenCalled();
120
147
  } );
121
148
 
122
- it( 'should not autosave when `editReference` changes and the post is not autosaving', () => {
149
+ it( 'should autosave edits made during an in-progress autosave once it finishes', () => {
123
150
  const autosave = jest.fn();
124
- const { rerender } = render(
125
- <AutosaveMonitor isAutosaveable autosave={ autosave } />
126
- );
127
-
151
+ setState( { isDirty: true, isAutosaveable: true } );
152
+ render( <AutosaveMonitor autosave={ autosave } interval={ 5 } /> );
153
+
154
+ // New edit arrives while an autosave is already in progress: the tick
155
+ // should not consume the edits reference nor trigger an autosave.
156
+ setState( {
157
+ isDirty: true,
158
+ isAutosaveable: true,
159
+ isAutosaving: true,
160
+ editsReference: 2,
161
+ } );
162
+ jest.advanceTimersByTime( 5000 );
128
163
  expect( autosave ).not.toHaveBeenCalled();
129
164
 
130
- rerender(
131
- <AutosaveMonitor
132
- isAutosaveable
133
- autosave={ autosave }
134
- isAutosaving={ false }
135
- editsReference={ [] }
136
- />
137
- );
138
-
139
- jest.runOnlyPendingTimers();
140
-
141
- expect( autosave ).not.toHaveBeenCalled();
165
+ // Once the autosave finishes, the pending edit must still be autosaved.
166
+ setState( { isDirty: true, isAutosaveable: true, editsReference: 2 } );
167
+ jest.advanceTimersByTime( 5000 );
168
+ expect( autosave ).toHaveBeenCalledTimes( 1 );
142
169
  } );
143
170
 
144
- it( 'should not autosave when `editReference` changes and the post started autosaving', () => {
171
+ it( 'should not autosave again until there are new edits', () => {
145
172
  const autosave = jest.fn();
146
- const { rerender } = render(
147
- <AutosaveMonitor
148
- isAutosaveable
149
- autosave={ autosave }
150
- isAutosaving={ false }
151
- />
152
- );
173
+ setState( { isDirty: true, isAutosaveable: true } );
174
+ render( <AutosaveMonitor autosave={ autosave } interval={ 5 } /> );
153
175
 
154
- expect( autosave ).not.toHaveBeenCalled();
155
-
156
- rerender(
157
- <AutosaveMonitor
158
- isAutosaveable
159
- autosave={ autosave }
160
- isAutosaving
161
- editsReference={ [] }
162
- />
163
- );
176
+ jest.advanceTimersByTime( 5000 );
177
+ expect( autosave ).toHaveBeenCalledTimes( 1 );
164
178
 
165
- jest.runOnlyPendingTimers();
179
+ // No new edits: a subsequent tick should not autosave again.
180
+ jest.advanceTimersByTime( 5000 );
181
+ expect( autosave ).toHaveBeenCalledTimes( 1 );
166
182
 
167
- expect( autosave ).not.toHaveBeenCalled();
183
+ // A new distinct edit triggers another autosave.
184
+ setState( { isDirty: true, isAutosaveable: true, editsReference: 2 } );
185
+ jest.advanceTimersByTime( 5000 );
186
+ expect( autosave ).toHaveBeenCalledTimes( 2 );
168
187
  } );
169
188
 
170
- it( 'should schedule itself in another {interval} ms', () => {
171
- const { rerender } = render( <AutosaveMonitor isDirty /> );
172
-
173
- rerender( <AutosaveMonitor isDirty isAutosaveable interval={ 5 } /> );
189
+ it( 'should keep pending edits and retry once the post becomes autosaveable', () => {
190
+ const autosave = jest.fn();
191
+ setState( { isDirty: true, isAutosaveable: false, editsReference: 2 } );
192
+ render( <AutosaveMonitor autosave={ autosave } interval={ 5 } /> );
174
193
 
175
- jest.runOnlyPendingTimers();
194
+ jest.advanceTimersByTime( 5000 );
195
+ expect( autosave ).not.toHaveBeenCalled();
176
196
 
177
- expect( setTimeout ).toHaveBeenLastCalledWith(
178
- expect.any( Function ),
179
- 5000
180
- );
197
+ setState( { isDirty: true, isAutosaveable: true, editsReference: 2 } );
198
+ jest.advanceTimersByTime( 5000 );
199
+ expect( autosave ).toHaveBeenCalledTimes( 1 );
181
200
  } );
182
201
 
183
- it( 'should schedule itself in 1000 ms if the post is not autosaveable at a time', () => {
184
- const { rerender } = render( <AutosaveMonitor isDirty /> );
185
-
186
- rerender(
187
- <AutosaveMonitor isDirty isAutosaveable={ false } interval={ 5 } />
188
- );
202
+ it( 'should fall back to the editor store autosave action', () => {
203
+ const autosave = jest.fn();
204
+ useDispatch.mockReturnValue( { autosave } );
205
+ setState( { isDirty: true, isAutosaveable: true } );
206
+ render( <AutosaveMonitor interval={ 5 } /> );
189
207
 
190
- jest.runOnlyPendingTimers();
208
+ jest.advanceTimersByTime( 5000 );
191
209
 
192
- expect( setTimeout ).toHaveBeenLastCalledWith(
193
- expect.any( Function ),
194
- 1000
195
- );
210
+ expect( autosave ).toHaveBeenCalledTimes( 1 );
196
211
  } );
197
212
  } );
@@ -19,6 +19,7 @@ exports[`PluginPostPublishPanel renders fill properly 1`] = `
19
19
  <svg
20
20
  aria-hidden="true"
21
21
  class="components-panel__arrow"
22
+ fill="currentColor"
22
23
  focusable="false"
23
24
  height="24"
24
25
  viewBox="0 0 24 24"
@@ -136,7 +136,6 @@ export default function PostPublishPanelPostpublish( {
136
136
  </p>
137
137
  <div className="post-publish-panel__postpublish-post-address-container">
138
138
  <TextControl
139
- __next40pxDefaultSize
140
139
  className="post-publish-panel__postpublish-post-address"
141
140
  readOnly
142
141
  label={ sprintf(
@@ -66,6 +66,7 @@ exports[`PostPublishPanel should render the post-publish panel if the post is pu
66
66
  >
67
67
  <svg
68
68
  aria-hidden="true"
69
+ fill="currentColor"
69
70
  focusable="false"
70
71
  height="24"
71
72
  viewBox="0 0 24 24"
@@ -134,7 +135,7 @@ exports[`PostPublishPanel should render the post-publish panel if the post is pu
134
135
  post address
135
136
  </label>
136
137
  <input
137
- class="components-text-control__input is-next-40px-default-size"
138
+ class="components-text-control__input"
138
139
  id="inspector-text-control-0"
139
140
  readonly=""
140
141
  type="text"
@@ -168,6 +169,7 @@ exports[`PostPublishPanel should render the post-publish panel if the post is pu
168
169
  </span>
169
170
  <svg
170
171
  aria-hidden="true"
172
+ fill="currentColor"
171
173
  focusable="false"
172
174
  height="24"
173
175
  viewBox="0 0 24 24"
@@ -291,6 +293,7 @@ exports[`PostPublishPanel should render the post-publish panel if the post is sc
291
293
  >
292
294
  <svg
293
295
  aria-hidden="true"
296
+ fill="currentColor"
294
297
  focusable="false"
295
298
  height="24"
296
299
  viewBox="0 0 24 24"
@@ -362,7 +365,7 @@ exports[`PostPublishPanel should render the post-publish panel if the post is sc
362
365
  post address
363
366
  </label>
364
367
  <input
365
- class="components-text-control__input is-next-40px-default-size"
368
+ class="components-text-control__input"
366
369
  id="inspector-text-control-1"
367
370
  readonly=""
368
371
  type="text"
@@ -521,6 +524,7 @@ exports[`PostPublishPanel should render the pre-publish panel if post status is
521
524
  <svg
522
525
  aria-hidden="true"
523
526
  class="components-site-icon"
527
+ fill="currentColor"
524
528
  focusable="false"
525
529
  height="36px"
526
530
  viewBox="-2 -2 24 24"
@@ -675,6 +679,7 @@ exports[`PostPublishPanel should render the pre-publish panel if the post is not
675
679
  <svg
676
680
  aria-hidden="true"
677
681
  class="components-site-icon"
682
+ fill="currentColor"
678
683
  focusable="false"
679
684
  height="36px"
680
685
  viewBox="-2 -2 24 24"
@@ -9,6 +9,7 @@ exports[`PostSavedState returns a disabled button if the post is not saveable 1`
9
9
  >
10
10
  <svg
11
11
  aria-hidden="true"
12
+ fill="currentColor"
12
13
  focusable="false"
13
14
  height="24"
14
15
  viewBox="0 0 24 24"
@@ -246,7 +246,6 @@ export default function PostStatus() {
246
246
  ) }
247
247
  type="text"
248
248
  id={ passwordInputId }
249
- __next40pxDefaultSize
250
249
  maxLength={ 255 }
251
250
  />
252
251
  </div>
@@ -462,7 +462,6 @@ export function HierarchicalTermSelector( { slug } ) {
462
462
  <form onSubmit={ onAddTerm }>
463
463
  <Flex direction="column" gap="4">
464
464
  <TextControl
465
- __next40pxDefaultSize
466
465
  className="editor-post-taxonomies__hierarchical-terms-input"
467
466
  label={ newTermLabel }
468
467
  value={ formName }
@@ -121,7 +121,6 @@ export default function CreateNewTemplateModal( { onClose } ) {
121
121
  <form onSubmit={ submit }>
122
122
  <VStack spacing="3">
123
123
  <TextControl
124
- __next40pxDefaultSize
125
124
  label={ __( 'Name' ) }
126
125
  value={ title }
127
126
  onChange={ setTitle }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { diffChars } from 'diff/lib/diff/character';
5
- import { diffLines } from 'diff/lib/diff/line';
4
+ import { diffChars } from 'diff/lib/diff/character.js';
5
+ import { diffLines } from 'diff/lib/diff/line.js';
6
6
 
7
7
  // Character diffing (Myers) is cheap when the edit distance is small but
8
8
  // degrades toward O(n^2) as the two strings diverge: a full replacement of
@@ -82,7 +82,6 @@ export default function PostVisibility( { onClose } ) {
82
82
  placeholder={ __( 'Use a secure password' ) }
83
83
  type="text"
84
84
  id={ `editor-post-visibility__password-input-${ instanceId }` }
85
- __next40pxDefaultSize
86
85
  maxLength={ 255 }
87
86
  />
88
87
  ) }