You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by gr...@apache.org on 2020/03/26 16:06:39 UTC
[incubator-superset] branch master updated: [dashboard] handle
markdown error (#9350)
This is an automated email from the ASF dual-hosted git repository.
graceguo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push:
new 5b07c8d [dashboard] handle markdown error (#9350)
5b07c8d is described below
commit 5b07c8d22901b7b173bd90f185904ea5c28bdad6
Author: Grace Guo <gr...@airbnb.com>
AuthorDate: Thu Mar 26 09:06:23 2020 -0700
[dashboard] handle markdown error (#9350)
* [dashboard] handle markdown error
* localize error message, fix review comments.
---
.../components/gridComponents/Markdown.jsx | 84 +++++++++++++++++++---
.../dashboard/containers/DashboardComponent.jsx | 4 ++
2 files changed, 77 insertions(+), 11 deletions(-)
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx b/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx
index 59137ba..31415dc 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx
@@ -23,6 +23,7 @@ import cx from 'classnames';
import AceEditor from 'react-ace';
import 'brace/mode/markdown';
import 'brace/theme/textmate';
+import { t } from '@superset-ui/translation';
import DeleteComponentButton from '../DeleteComponentButton';
import DragDroppable from '../dnd/DragDroppable';
@@ -49,6 +50,9 @@ const propTypes = {
// from redux
logEvent: PropTypes.func.isRequired,
+ addDangerToast: PropTypes.func.isRequired,
+ undoLength: PropTypes.number.isRequired,
+ redoLength: PropTypes.number.isRequired,
// grid related
availableColumnCount: PropTypes.number.isRequired,
@@ -65,7 +69,7 @@ const propTypes = {
const defaultProps = {};
-const markdownPlaceHolder = `# ✨Markdown
+const MARKDOWN_PLACE_HOLDER = `# ✨Markdown
## ✨Markdown
### ✨Markdown
@@ -73,6 +77,8 @@ const markdownPlaceHolder = `# ✨Markdown
Click here to edit [markdown](https://bit.ly/1dQOfRK)`;
+const MARKDOWN_ERROR_MESSAGE = t('This markdown component has an error.');
+
function isSafeMarkup(node) {
if (node.type === 'html') {
return /href="(javascript|vbscript|file):.*"/gim.test(node.value) === false;
@@ -88,6 +94,8 @@ class Markdown extends React.PureComponent {
markdownSource: props.component.meta.code,
editor: null,
editorMode: 'preview',
+ undoLength: props.undoLength,
+ redoLength: props.redoLength,
};
this.renderStartTime = Logger.getTimestamp();
@@ -107,11 +115,46 @@ class Markdown extends React.PureComponent {
});
}
- UNSAFE_componentWillReceiveProps(nextProps) {
- const nextSource = nextProps.component.meta.code;
- if (this.state.markdownSource !== nextSource) {
- this.setState({ markdownSource: nextSource });
+ static getDerivedStateFromProps(nextProps, state) {
+ const {
+ hasError,
+ editorMode,
+ markdownSource,
+ undoLength,
+ redoLength,
+ } = state;
+ const {
+ component: nextComponent,
+ undoLength: nextUndoLength,
+ redoLength: nextRedoLength,
+ } = nextProps;
+ // user click undo or redo ?
+ if (nextUndoLength !== undoLength || nextRedoLength !== redoLength) {
+ return {
+ ...state,
+ undoLength: nextUndoLength,
+ redoLength: nextRedoLength,
+ markdownSource: nextComponent.meta.code,
+ hasError: false,
+ };
+ } else if (
+ !hasError &&
+ editorMode === 'preview' &&
+ nextComponent.meta.code !== markdownSource
+ ) {
+ return {
+ ...state,
+ markdownSource: nextComponent.meta.code,
+ };
}
+
+ return state;
+ }
+
+ static getDerivedStateFromError() {
+ return {
+ hasError: true,
+ };
}
componentDidUpdate(prevProps) {
@@ -124,6 +167,16 @@ class Markdown extends React.PureComponent {
}
}
+ componentDidCatch(error, info) {
+ if (this.state.editor && this.state.editorMode === 'preview') {
+ this.props.addDangerToast(
+ t(
+ 'This markdown component has an error. Please revert your recent changes.',
+ ),
+ );
+ }
+ }
+
setEditor(editor) {
editor.getSession().setUseWrapMode(true);
this.setState({
@@ -139,7 +192,12 @@ class Markdown extends React.PureComponent {
}
handleChangeEditorMode(mode) {
- if (this.state.editorMode === 'edit') {
+ const nextState = {
+ ...this.state,
+ editorMode: mode,
+ };
+
+ if (mode === 'preview') {
const { updateComponents, component } = this.props;
if (component.meta.code !== this.state.markdownSource) {
updateComponents({
@@ -152,11 +210,10 @@ class Markdown extends React.PureComponent {
},
});
}
+ nextState.hasError = false;
}
- this.setState(() => ({
- editorMode: mode,
- }));
+ this.setState(nextState);
}
handleMarkdownChange(nextValue) {
@@ -184,7 +241,7 @@ class Markdown extends React.PureComponent {
// thisl allows "select all => delete" to give an empty editor
typeof this.state.markdownSource === 'string'
? this.state.markdownSource
- : markdownPlaceHolder
+ : MARKDOWN_PLACE_HOLDER
}
readOnly={false}
onLoad={this.setEditor}
@@ -193,9 +250,14 @@ class Markdown extends React.PureComponent {
}
renderPreviewMode() {
+ const { hasError } = this.state;
return (
<ReactMarkdown
- source={this.state.markdownSource || markdownPlaceHolder}
+ source={
+ hasError
+ ? MARKDOWN_ERROR_MESSAGE
+ : this.state.markdownSource || MARKDOWN_PLACE_HOLDER
+ }
escapeHtml={false}
allowNode={isSafeMarkup}
/>
diff --git a/superset-frontend/src/dashboard/containers/DashboardComponent.jsx b/superset-frontend/src/dashboard/containers/DashboardComponent.jsx
index dd2c188..e0eb38a 100644
--- a/superset-frontend/src/dashboard/containers/DashboardComponent.jsx
+++ b/superset-frontend/src/dashboard/containers/DashboardComponent.jsx
@@ -35,6 +35,7 @@ import {
} from '../actions/dashboardLayout';
import { setDirectPathToChild } from '../actions/dashboardState';
import { logEvent } from '../../logger/actions';
+import { addDangerToast } from '../../messageToasts/actions';
const propTypes = {
component: componentShape.isRequired,
@@ -66,6 +67,8 @@ function mapStateToProps(
component,
parentComponent: dashboardLayout[parentId],
editMode: dashboardState.editMode,
+ undoLength: undoableLayout.past.length,
+ redoLength: undoableLayout.future.length,
filters: getActiveFilters(),
directPathToChild: dashboardState.directPathToChild,
directPathLastUpdated: dashboardState.directPathLastUpdated,
@@ -97,6 +100,7 @@ function mapStateToProps(
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
+ addDangerToast,
createComponent,
deleteComponent,
updateComponents,