Skip to content

Commit

Permalink
Editor: Factor initial edits to own state (#11267)
Browse files Browse the repository at this point in the history
* Editor: Factor initial edits to own state

* Testing: Verify immediate saveability of demo post

* URL: Test undefined values as ignored

* URL: Test space encoding

* Testing: Verify saveability, non-dirtiness of initial edits
  • Loading branch information
aduth authored Nov 6, 2018
1 parent 2dddcc9 commit 8cbfb3c
Show file tree
Hide file tree
Showing 17 changed files with 462 additions and 136 deletions.
11 changes: 10 additions & 1 deletion docs/data/data-core-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,7 @@ the specified post object and editor settings.
*Parameters*

* post: Post object.
* edits: Initial edited attributes object.

### resetPost

Expand Down Expand Up @@ -1427,7 +1428,6 @@ Returns an action object used to setup the editor state when first opening an ed

* post: Post object.
* blocks: Array of blocks.
* edits: Initial edited attributes object.

### resetBlocks

Expand Down Expand Up @@ -1574,6 +1574,15 @@ Returns an action object resetting the template validity.

Returns an action object synchronize the template with the list of blocks

### editPost

Returns an action object used in signalling that attributes of the post have
been edited.

*Parameters*

* edits: Post attributes to edit.

### savePost

Returns an action object to save the post.
Expand Down
20 changes: 5 additions & 15 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -1467,26 +1467,16 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
$demo_content = ob_get_clean();

$initial_edits = array(
'title' => array(
'raw' => __( 'Welcome to the Gutenberg Editor', 'gutenberg' ),
),
'content' => array(
'raw' => $demo_content,
),
'title' => __( 'Welcome to the Gutenberg Editor', 'gutenberg' ),
'content' => $demo_content,
);
} elseif ( $is_new_post ) {
// Override "(Auto Draft)" new post default title with empty string,
// or filtered value.
$initial_edits = array(
'title' => array(
'raw' => $post->post_title,
),
'content' => array(
'raw' => $post->post_content,
),
'excerpt' => array(
'raw' => $post->post_excerpt,
),
'title' => $post->post_title,
'content' => $post->post_content,
'excerpt' => $post->post_excerpt,
);
} else {
$initial_edits = null;
Expand Down
5 changes: 3 additions & 2 deletions packages/edit-post/src/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function Editor( {
hasFixedToolbar,
focusMode,
post,
overridePost,
initialEdits,
onError,
...props
} ) {
Expand All @@ -32,7 +32,8 @@ function Editor( {
<StrictMode>
<EditorProvider
settings={ editorSettings }
post={ { ...post, ...overridePost } }
post={ post }
initialEdits={ initialEdits }
{ ...props }
>
<ErrorBoundary onError={ onError }>
Expand Down
49 changes: 33 additions & 16 deletions packages/edit-post/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,27 @@ import Editor from './editor';
* an unhandled error occurs, replacing previously mounted editor element using
* an initial state from prior to the crash.
*
* @param {Object} postType Post type of the post to edit.
* @param {Object} postId ID of the post to edit.
* @param {Element} target DOM node in which editor is rendered.
* @param {?Object} settings Editor settings object.
* @param {Object} overridePost Post properties to override.
* @param {Object} postType Post type of the post to edit.
* @param {Object} postId ID of the post to edit.
* @param {Element} target DOM node in which editor is rendered.
* @param {?Object} settings Editor settings object.
* @param {Object} initialEdits Programmatic edits to apply initially, to be
* considered as non-user-initiated (bypass for
* unsaved changes prompt).
*/
export function reinitializeEditor( postType, postId, target, settings, overridePost ) {
export function reinitializeEditor( postType, postId, target, settings, initialEdits ) {
unmountComponentAtNode( target );
const reboot = reinitializeEditor.bind( null, postType, postId, target, settings, overridePost );
const reboot = reinitializeEditor.bind( null, postType, postId, target, settings, initialEdits );

render(
<Editor settings={ settings } onError={ reboot } postId={ postId } postType={ postType } overridePost={ overridePost } recovery />,
<Editor
settings={ settings }
onError={ reboot }
postId={ postId }
postType={ postType }
initialEdits={ initialEdits }
recovery
/>,
target
);
}
Expand All @@ -44,15 +53,17 @@ export function reinitializeEditor( postType, postId, target, settings, override
* The return value of this function is not necessary if we change where we
* call initializeEditor(). This is due to metaBox timing.
*
* @param {string} id Unique identifier for editor instance.
* @param {Object} postType Post type of the post to edit.
* @param {Object} postId ID of the post to edit.
* @param {?Object} settings Editor settings object.
* @param {Object} overridePost Post properties to override.
* @param {string} id Unique identifier for editor instance.
* @param {Object} postType Post type of the post to edit.
* @param {Object} postId ID of the post to edit.
* @param {?Object} settings Editor settings object.
* @param {Object} initialEdits Programmatic edits to apply initially, to be
* considered as non-user-initiated (bypass for
* unsaved changes prompt).
*/
export function initializeEditor( id, postType, postId, settings, overridePost ) {
export function initializeEditor( id, postType, postId, settings, initialEdits ) {
const target = document.getElementById( id );
const reboot = reinitializeEditor.bind( null, postType, postId, target, settings, overridePost );
const reboot = reinitializeEditor.bind( null, postType, postId, target, settings, initialEdits );

registerCoreBlocks();

Expand All @@ -64,7 +75,13 @@ export function initializeEditor( id, postType, postId, settings, overridePost )
] );

render(
<Editor settings={ settings } onError={ reboot } postId={ postId } postType={ postType } overridePost={ overridePost } />,
<Editor
settings={ settings }
onError={ reboot }
postId={ postId }
postType={ postType }
initialEdits={ initialEdits }
/>,
target
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/components/provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class EditorProvider extends Component {

props.updateEditorSettings( props.settings );
props.updatePostLock( props.settings.postLock );
props.setupEditor( props.post );
props.setupEditor( props.post, props.initialEdits );

if ( props.settings.autosave ) {
props.createWarningNotice(
Expand Down
22 changes: 15 additions & 7 deletions packages/editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ import { dispatch } from '@wordpress/data';
* Returns an action object used in signalling that editor has initialized with
* the specified post object and editor settings.
*
* @param {Object} post Post object.
* @param {Object} post Post object.
* @param {Object} edits Initial edited attributes object.
*
* @return {Object} Action object.
*/
export function setupEditor( post ) {
export function setupEditor( post, edits ) {
return {
type: 'SETUP_EDITOR',
post,
edits,
};
}

Expand Down Expand Up @@ -76,18 +78,16 @@ export function updatePost( edits ) {
/**
* Returns an action object used to setup the editor state when first opening an editor.
*
* @param {Object} post Post object.
* @param {Array} blocks Array of blocks.
* @param {Object} edits Initial edited attributes object.
* @param {Object} post Post object.
* @param {Array} blocks Array of blocks.
*
* @return {Object} Action object.
*/
export function setupEditorState( post, blocks, edits ) {
export function setupEditorState( post, blocks ) {
return {
type: 'SETUP_EDITOR_STATE',
post,
blocks,
edits,
};
}

Expand Down Expand Up @@ -381,6 +381,14 @@ export function synchronizeTemplate() {
};
}

/**
* Returns an action object used in signalling that attributes of the post have
* been edited.
*
* @param {Object} edits Post attributes to edit.
*
* @return {Object} Action object.
*/
export function editPost( edits ) {
return {
type: 'EDIT_POST',
Expand Down
7 changes: 7 additions & 0 deletions packages/editor/src/store/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,10 @@ export const EDITOR_SETTINGS_DEFAULTS = {
// List of allowed mime types and file extensions.
allowedMimeTypes: null,
};

/**
* Default initial edits state.
*
* @type {Object}
*/
export const INITIAL_EDITS_DEFAULTS = {};
25 changes: 14 additions & 11 deletions packages/editor/src/store/effects.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { compact, last } from 'lodash';
import { compact, last, has } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -200,11 +200,20 @@ export default {
) );
},
SETUP_EDITOR( action, store ) {
const { post } = action;
const { post, edits } = action;
const state = store.getState();

// Parse content as blocks
let blocks = parse( post.content.raw );
// In order to ensure maximum of a single parse during setup, edits are
// included as part of editor setup action. Assume edited content as
// canonical if provided, falling back to post.
let content;
if ( has( edits, [ 'content' ] ) ) {
content = edits.content;
} else {
content = post.content.raw;
}

let blocks = parse( content );

// Apply a template for new posts only, if exists.
const isNewPost = post.status === 'auto-draft';
Expand All @@ -213,13 +222,7 @@ export default {
blocks = synchronizeBlocksWithTemplate( blocks, template );
}

// Include auto draft title in edits while not flagging post as dirty
const edits = {};
if ( isNewPost ) {
edits.title = post.title.raw;
}

const setupAction = setupEditorState( post, blocks, edits );
const setupAction = setupEditorState( post, blocks );

return compact( [
setupAction,
Expand Down
Loading

0 comments on commit 8cbfb3c

Please sign in to comment.