diff --git a/components/clipboard-button/index.js b/components/clipboard-button/index.js
index 80a96920e5cbd1..3c4a78f30783a0 100644
--- a/components/clipboard-button/index.js
+++ b/components/clipboard-button/index.js
@@ -57,19 +57,26 @@ class ClipboardButton extends Component {
}
getText() {
- return this.props.text;
+ let text = this.props.text;
+ if ( 'function' === typeof text ) {
+ text = text();
+ }
+
+ return text;
}
render() {
- const { className, children } = this.props;
+ // Disable reason: Exclude from spread props passed to Button
+ // eslint-disable-next-line no-unused-vars
+ const { className, children, onCopy, text, ...buttonProps } = this.props;
const classes = classnames( 'components-clipboard-button', className );
return (
-
+
{ children }
);
}
-export default BlockWarning;
+export default Warning;
diff --git a/editor/components/warning/style.scss b/editor/components/warning/style.scss
new file mode 100644
index 00000000000000..4f9d7119ab0937
--- /dev/null
+++ b/editor/components/warning/style.scss
@@ -0,0 +1,29 @@
+.editor-warning {
+ z-index: z-index( '.editor-warning' );
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate( -50%, -50% );
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ align-items: center;
+ width: 96%;
+ max-width: 780px;
+ padding: 20px 20px 10px 20px;
+ background-color: $white;
+ border: 1px solid $light-gray-500;
+ text-align: center;
+ line-height: $default-line-height;
+ box-shadow: $shadow-popover;
+
+ .editor-visual-editor & p {
+ width: 100%;
+ font-family: $default-font;
+ font-size: $default-font-size;
+ }
+
+ .components-button {
+ margin: 0 #{ $item-spacing / 2 } 5px;
+ }
+}
diff --git a/editor/index.js b/editor/index.js
index 5b9883d12c3af8..4091f164981cfa 100644
--- a/editor/index.js
+++ b/editor/index.js
@@ -7,7 +7,7 @@ import 'moment-timezone/moment-timezone-utils';
/**
* WordPress dependencies
*/
-import { render } from '@wordpress/element';
+import { render, unmountComponentAtNode } from '@wordpress/element';
import { settings as dateSettings } from '@wordpress/date';
/**
@@ -15,7 +15,7 @@ import { settings as dateSettings } from '@wordpress/date';
*/
import './assets/stylesheets/main.scss';
import Layout from './layout';
-import { EditorProvider } from './components';
+import { EditorProvider, ErrorBoundary } from './components';
import { initializeMetaBoxState } from './actions';
export * from './components';
@@ -45,23 +45,49 @@ window.jQuery( document ).on( 'heartbeat-tick', ( event, response ) => {
}
} );
+/**
+ * Reinitializes the editor after the user chooses to reboot the editor after
+ * an unhandled error occurs, replacing previously mounted editor element using
+ * an initial state from prior to the crash.
+ *
+ * @param {Element} target DOM node in which editor is rendered
+ * @param {*} initialState Initial editor state to hydrate
+ */
+export function recreateEditorInstance( target, initialState ) {
+ unmountComponentAtNode( target );
+
+ const reboot = recreateEditorInstance.bind( null, target );
+
+ render(
+
+
+
+
+ ,
+ target
+ );
+}
+
/**
* Initializes and returns an instance of Editor.
*
* The return value of this function is not necessary if we change where we
* call createEditorInstance(). This is due to metaBox timing.
*
- * @param {String} id Unique identifier for editor instance
- * @param {Object} post API entity for post to edit
- * @param {?Object} settings Editor settings object
- * @return {Object} Editor interface. Currently supports metabox initialization.
+ * @param {String} id Unique identifier for editor instance
+ * @param {Object} post API entity for post to edit
+ * @param {?Object} settings Editor settings object
+ * @return {Object} Editor interface
*/
export function createEditorInstance( id, post, settings ) {
const target = document.getElementById( id );
+ const reboot = recreateEditorInstance.bind( null, target );
const provider = render(
-
+
+
+
,
target
);
diff --git a/editor/modes/visual-editor/block-crash-warning.js b/editor/modes/visual-editor/block-crash-warning.js
index b1834926026460..52dcab22852dd7 100644
--- a/editor/modes/visual-editor/block-crash-warning.js
+++ b/editor/modes/visual-editor/block-crash-warning.js
@@ -6,14 +6,14 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import BlockWarning from './block-warning';
+import { Warning } from '../../components';
const warning = (
-
+
{ __(
- 'This block has suffered from an unhandled error and cannot be previewed.'
+ 'This block has encountered an error and cannot be previewed.'
) }
-
+
);
export default () => warning;
diff --git a/editor/modes/visual-editor/invalid-block-warning.js b/editor/modes/visual-editor/invalid-block-warning.js
index 26d580b07dabcc..2ac3b64a65046d 100644
--- a/editor/modes/visual-editor/invalid-block-warning.js
+++ b/editor/modes/visual-editor/invalid-block-warning.js
@@ -12,7 +12,7 @@ import { Button } from '@wordpress/components';
/**
* Internal dependencies
*/
-import BlockWarning from './block-warning';
+import { Warning } from '../../components';
import {
getBlockType,
getUnknownTypeHandlerName,
@@ -31,7 +31,7 @@ function InvalidBlockWarning( { ignoreInvalid, switchToBlockType } ) {
const switchTo = ( blockType ) => () => switchToBlockType( blockType );
return (
-
+
{ defaultBlockType && htmlBlockType && sprintf( __(
'This block appears to have been modified externally. ' +
'Overwrite the external changes or Convert to %s or %s to keep ' +
@@ -67,7 +67,7 @@ function InvalidBlockWarning( { ignoreInvalid, switchToBlockType } ) {
) }
-
+
);
}
diff --git a/editor/modes/visual-editor/style.scss b/editor/modes/visual-editor/style.scss
index 29d6de04bbb28e..8dc799a27991e3 100644
--- a/editor/modes/visual-editor/style.scss
+++ b/editor/modes/visual-editor/style.scss
@@ -51,7 +51,7 @@
position: relative;
min-height: 250px;
- > :not( .editor-visual-editor__block-warning ) {
+ > :not( .editor-warning ) {
pointer-events: none;
user-select: none;
}
@@ -421,40 +421,6 @@
height: 20px;
}
-.editor-visual-editor__block-warning {
- z-index: z-index( '.editor-visual-editor__block-warning' );
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate( -50%, -50% );
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- align-items: center;
- width: 96%;
- max-width: 780px;
- padding: 20px 20px 10px 20px;
- background-color: $white;
- border: 1px solid $light-gray-500;
- text-align: center;
- line-height: $default-line-height;
- box-shadow: $shadow-popover;
-
- p {
- width: 100%;
- font-family: $default-font;
- font-size: $default-font-size;
- }
-
- .button + .button {
- margin-left: $item-spacing;
- }
-}
-
-.visual-editor__invalid-block-warning-buttons .components-button {
- margin-bottom: 5px;
-}
-
.editor-visual-editor__block .blocks-visual-editor__block-html-textarea {
display: block;
margin: 0;
diff --git a/editor/store.js b/editor/store.js
index fa2e7db7dfc7c4..3dbc97fd5220c3 100644
--- a/editor/store.js
+++ b/editor/store.js
@@ -21,9 +21,10 @@ const GUTENBERG_PREFERENCES_KEY = `GUTENBERG_PREFERENCES_${ window.userSettings.
/**
* Creates a new instance of a Redux store.
*
- * @return {Redux.Store} Redux store
+ * @param {?*} preloadedState Optional initial state
+ * @return {Redux.Store} Redux store
*/
-function createReduxStore() {
+function createReduxStore( preloadedState ) {
const enhancers = [
applyMiddleware( multi, refx( effects ) ),
storePersist( 'preferences', GUTENBERG_PREFERENCES_KEY ),
@@ -33,7 +34,7 @@ function createReduxStore() {
enhancers.push( window.__REDUX_DEVTOOLS_EXTENSION__() );
}
- const store = createStore( reducer, flowRight( enhancers ) );
+ const store = createStore( reducer, preloadedState, flowRight( enhancers ) );
return store;
}
diff --git a/element/index.js b/element/index.js
index 97c8ef990fd63f..0826db19020dd6 100644
--- a/element/index.js
+++ b/element/index.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import { createElement, Component, cloneElement, Children } from 'react';
-import { render, findDOMNode, createPortal } from 'react-dom';
+import { render, findDOMNode, createPortal, unmountComponentAtNode } from 'react-dom';
import { renderToStaticMarkup } from 'react-dom/server';
import { isString } from 'lodash';
@@ -27,6 +27,13 @@ export { createElement };
*/
export { render };
+/**
+ * Removes any mounted element from the target DOM node.
+ *
+ * @param {Element} target DOM node in which element is to be removed
+ */
+export { unmountComponentAtNode };
+
/**
* A base class to create WordPress Components (Refs, state and lifecycle hooks)
*/