@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.
- package/CHANGELOG.md +2 -0
- package/README.md +5 -13
- package/build/components/autosave-monitor/index.cjs +40 -83
- package/build/components/autosave-monitor/index.cjs.map +3 -3
- package/build/components/post-publish-panel/postpublish.cjs +0 -1
- package/build/components/post-publish-panel/postpublish.cjs.map +2 -2
- package/build/components/post-status/index.cjs +0 -1
- package/build/components/post-status/index.cjs.map +2 -2
- package/build/components/post-taxonomies/hierarchical-term-selector.cjs +0 -1
- package/build/components/post-taxonomies/hierarchical-term-selector.cjs.map +2 -2
- package/build/components/post-template/create-new-template-modal.cjs +0 -1
- package/build/components/post-template/create-new-template-modal.cjs.map +2 -2
- package/build/components/post-text-editor/utils.cjs +2 -2
- package/build/components/post-text-editor/utils.cjs.map +1 -1
- package/build/components/post-visibility/index.cjs +0 -1
- package/build/components/post-visibility/index.cjs.map +2 -2
- package/build/components/provider/disable-non-page-content-blocks.cjs +23 -3
- package/build/components/provider/disable-non-page-content-blocks.cjs.map +2 -2
- package/build/components/provider/navigation-block-editing-mode.cjs +9 -2
- package/build/components/provider/navigation-block-editing-mode.cjs.map +2 -2
- package/build/components/provider/use-block-editor-settings.cjs +2 -1
- package/build/components/provider/use-block-editor-settings.cjs.map +2 -2
- package/build/components/sidebar/dataform-post-summary.cjs +87 -32
- package/build/components/sidebar/dataform-post-summary.cjs.map +3 -3
- package/build/components/sidebar/post-summary.cjs +8 -6
- package/build/components/sidebar/post-summary.cjs.map +3 -3
- package/build/dataviews/store/private-actions.cjs +3 -1
- package/build/dataviews/store/private-actions.cjs.map +2 -2
- package/build/store/private-actions.cjs +2 -2
- package/build/store/private-actions.cjs.map +2 -2
- package/build-module/components/autosave-monitor/index.mjs +42 -81
- package/build-module/components/autosave-monitor/index.mjs.map +2 -2
- package/build-module/components/post-publish-panel/postpublish.mjs +0 -1
- package/build-module/components/post-publish-panel/postpublish.mjs.map +2 -2
- package/build-module/components/post-status/index.mjs +0 -1
- package/build-module/components/post-status/index.mjs.map +2 -2
- package/build-module/components/post-taxonomies/hierarchical-term-selector.mjs +0 -1
- package/build-module/components/post-taxonomies/hierarchical-term-selector.mjs.map +2 -2
- package/build-module/components/post-template/create-new-template-modal.mjs +0 -1
- package/build-module/components/post-template/create-new-template-modal.mjs.map +2 -2
- package/build-module/components/post-text-editor/utils.mjs +2 -2
- package/build-module/components/post-text-editor/utils.mjs.map +1 -1
- package/build-module/components/post-visibility/index.mjs +0 -1
- package/build-module/components/post-visibility/index.mjs.map +2 -2
- package/build-module/components/provider/disable-non-page-content-blocks.mjs +23 -3
- package/build-module/components/provider/disable-non-page-content-blocks.mjs.map +2 -2
- package/build-module/components/provider/navigation-block-editing-mode.mjs +10 -3
- package/build-module/components/provider/navigation-block-editing-mode.mjs.map +2 -2
- package/build-module/components/provider/use-block-editor-settings.mjs +2 -1
- package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
- package/build-module/components/sidebar/dataform-post-summary.mjs +88 -33
- package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
- package/build-module/components/sidebar/post-summary.mjs +8 -6
- package/build-module/components/sidebar/post-summary.mjs.map +2 -2
- package/build-module/dataviews/store/private-actions.mjs +5 -1
- package/build-module/dataviews/store/private-actions.mjs.map +2 -2
- package/build-module/store/private-actions.mjs +2 -2
- package/build-module/store/private-actions.mjs.map +2 -2
- package/build-style/style-rtl.css +29 -33
- package/build-style/style.css +29 -33
- package/build-types/components/autosave-monitor/index.d.ts +11 -29
- package/build-types/components/autosave-monitor/index.d.ts.map +1 -1
- package/build-types/components/post-publish-panel/postpublish.d.ts.map +1 -1
- package/build-types/components/post-status/index.d.ts.map +1 -1
- package/build-types/components/post-taxonomies/hierarchical-term-selector.d.ts.map +1 -1
- package/build-types/components/post-template/create-new-template-modal.d.ts.map +1 -1
- package/build-types/components/post-visibility/index.d.ts.map +1 -1
- package/build-types/components/provider/disable-non-page-content-blocks.d.ts.map +1 -1
- package/build-types/components/provider/navigation-block-editing-mode.d.ts.map +1 -1
- package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
- package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -1
- package/build-types/components/sidebar/post-summary.d.ts.map +1 -1
- package/build-types/dataviews/store/private-actions.d.ts.map +1 -1
- package/build-types/store/selectors.d.ts.map +1 -1
- package/build-types/utils/get-template-part-icon.d.ts.map +1 -1
- package/package.json +46 -46
- package/src/components/autosave-monitor/index.js +69 -104
- package/src/components/autosave-monitor/test/index.js +137 -122
- package/src/components/plugin-post-publish-panel/test/__snapshots__/index.js.snap +1 -0
- package/src/components/post-publish-panel/postpublish.js +0 -1
- package/src/components/post-publish-panel/test/__snapshots__/index.js.snap +7 -2
- package/src/components/post-saved-state/test/__snapshots__/index.js.snap +1 -0
- package/src/components/post-status/index.js +0 -1
- package/src/components/post-taxonomies/hierarchical-term-selector.js +0 -1
- package/src/components/post-template/create-new-template-modal.js +0 -1
- package/src/components/post-text-editor/utils.js +2 -2
- package/src/components/post-visibility/index.js +0 -1
- package/src/components/provider/disable-non-page-content-blocks.js +23 -6
- package/src/components/provider/navigation-block-editing-mode.js +10 -4
- package/src/components/provider/use-block-editor-settings.js +3 -1
- package/src/components/sidebar/dataform-post-summary.js +133 -49
- package/src/components/sidebar/post-summary.js +12 -10
- package/src/dataviews/store/private-actions.ts +8 -1
- package/src/store/private-actions.js +2 -2
- package/src/store/test/actions.js +2 -2
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}, timeout );
|
|
62
|
-
}
|
|
23
|
+
useEffect( () => {
|
|
24
|
+
callbackRef.current = callback;
|
|
25
|
+
}, [ callback ] );
|
|
63
26
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
27
|
+
useEffect( () => {
|
|
28
|
+
// Interval can be undefined before editor settings are populated.
|
|
29
|
+
if ( ! intervalInSeconds ) {
|
|
67
30
|
return;
|
|
68
31
|
}
|
|
69
32
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
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
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
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={
|
|
54
|
+
* <AutosaveMonitor interval={ 30 } />
|
|
104
55
|
* ```
|
|
105
56
|
*/
|
|
106
|
-
export default
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
getEditorSettings,
|
|
115
|
-
} = select( editorStore );
|
|
71
|
+
return select( editorStore ).getEditorSettings().autosaveInterval;
|
|
72
|
+
},
|
|
73
|
+
[ interval ]
|
|
74
|
+
);
|
|
116
75
|
|
|
117
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
autosave
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
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, '
|
|
13
|
-
jest.spyOn( global, '
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
|
73
|
+
it( 'should start the autosave timer after being mounted', () => {
|
|
74
|
+
render( <AutosaveMonitor /> );
|
|
36
75
|
|
|
37
|
-
expect(
|
|
76
|
+
expect( setInterval ).toHaveBeenCalled();
|
|
38
77
|
} );
|
|
39
78
|
|
|
40
79
|
it( 'should clear the autosave timer after being unmounted', () => {
|
|
41
|
-
const { rerender } = render( <AutosaveMonitor
|
|
80
|
+
const { rerender } = render( <AutosaveMonitor /> );
|
|
42
81
|
|
|
43
82
|
rerender( <div /> );
|
|
44
83
|
|
|
45
|
-
expect(
|
|
84
|
+
expect( clearInterval ).toHaveBeenCalled();
|
|
46
85
|
} );
|
|
47
86
|
|
|
48
|
-
it( 'should
|
|
49
|
-
const { rerender } = render( <AutosaveMonitor
|
|
87
|
+
it( 'should restart the autosave timer when the interval changes', () => {
|
|
88
|
+
const { rerender } = render( <AutosaveMonitor interval={ 10 } /> );
|
|
50
89
|
|
|
51
|
-
|
|
90
|
+
setInterval.mockClear();
|
|
91
|
+
clearInterval.mockClear();
|
|
92
|
+
rerender( <AutosaveMonitor interval={ 999 } /> );
|
|
52
93
|
|
|
53
|
-
expect(
|
|
54
|
-
expect(
|
|
94
|
+
expect( clearInterval ).toHaveBeenCalled();
|
|
95
|
+
expect( setInterval ).toHaveBeenCalledTimes( 1 );
|
|
55
96
|
} );
|
|
56
97
|
|
|
57
|
-
it( 'should
|
|
58
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
isAutosaveable
|
|
69
|
-
autosave={ autosave }
|
|
70
|
-
editsReference={ [] }
|
|
71
|
-
/>
|
|
101
|
+
expect( setInterval ).toHaveBeenLastCalledWith(
|
|
102
|
+
expect.any( Function ),
|
|
103
|
+
5000
|
|
72
104
|
);
|
|
105
|
+
} );
|
|
73
106
|
|
|
74
|
-
|
|
107
|
+
it( 'should fall back to the editor autosaveInterval setting', () => {
|
|
108
|
+
setState( { autosaveInterval: 7 } );
|
|
109
|
+
render( <AutosaveMonitor /> );
|
|
75
110
|
|
|
76
|
-
expect(
|
|
111
|
+
expect( setInterval ).toHaveBeenLastCalledWith(
|
|
112
|
+
expect.any( Function ),
|
|
113
|
+
7000
|
|
114
|
+
);
|
|
77
115
|
} );
|
|
78
116
|
|
|
79
|
-
it( 'should autosave
|
|
117
|
+
it( 'should autosave a dirty, autosaveable post on the timer tick', () => {
|
|
80
118
|
const autosave = jest.fn();
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
|
129
|
+
it( 'should not autosave when the post is not dirty', () => {
|
|
102
130
|
const autosave = jest.fn();
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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.
|
|
144
|
+
jest.advanceTimersByTime( 5000 );
|
|
118
145
|
|
|
119
146
|
expect( autosave ).not.toHaveBeenCalled();
|
|
120
147
|
} );
|
|
121
148
|
|
|
122
|
-
it( 'should
|
|
149
|
+
it( 'should autosave edits made during an in-progress autosave once it finishes', () => {
|
|
123
150
|
const autosave = jest.fn();
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
171
|
+
it( 'should not autosave again until there are new edits', () => {
|
|
145
172
|
const autosave = jest.fn();
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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
|
-
|
|
179
|
+
// No new edits: a subsequent tick should not autosave again.
|
|
180
|
+
jest.advanceTimersByTime( 5000 );
|
|
181
|
+
expect( autosave ).toHaveBeenCalledTimes( 1 );
|
|
166
182
|
|
|
167
|
-
|
|
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
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
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.
|
|
194
|
+
jest.advanceTimersByTime( 5000 );
|
|
195
|
+
expect( autosave ).not.toHaveBeenCalled();
|
|
176
196
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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.
|
|
208
|
+
jest.advanceTimersByTime( 5000 );
|
|
191
209
|
|
|
192
|
-
expect(
|
|
193
|
-
expect.any( Function ),
|
|
194
|
-
1000
|
|
195
|
-
);
|
|
210
|
+
expect( autosave ).toHaveBeenCalledTimes( 1 );
|
|
196
211
|
} );
|
|
197
212
|
} );
|
|
@@ -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
|
|
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
|
|
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"
|
|
@@ -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 }
|
|
@@ -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
|