mirror of https://github.com/apache/superset.git
[dashboard] handle markdown error (#9350)
* [dashboard] handle markdown error * localize error message, fix review comments.
This commit is contained in:
parent
d5e2f7e6b3
commit
5b07c8d229
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue