From 860d2865e526a3805eb0da3fd4aff00df354040c Mon Sep 17 00:00:00 2001
From: xfause <919017553@qq.com>
Date: Fri, 20 Mar 2020 17:17:34 +0800
Subject: [PATCH 1/2] feat(onError): add handleError
add handleError
can get two params in onError
preJson: latest correct json
error: error from json.parse()
---
stories/Editor.jsx | 335 +++++++++++++++++++++++----------------------
1 file changed, 168 insertions(+), 167 deletions(-)
diff --git a/stories/Editor.jsx b/stories/Editor.jsx
index ab7953a..034344c 100644
--- a/stories/Editor.jsx
+++ b/stories/Editor.jsx
@@ -1,167 +1,168 @@
-import React from 'react';
-import ace from 'brace';
-import 'brace/mode/json';
-import 'brace/theme/tomorrow_night_blue';
-import { Form, Field, reduxForm } from 'redux-form';
-import { storiesOf } from '@storybook/react';
-import { action } from '@storybook/addon-actions';
-import Ajv from 'ajv';
-import Editor from '../src/Editor';
-import Decorator from './decorator';
-import { reduxDecorator } from './reduxDecorator';
-import { FieldComponent } from './FieldComponent';
-
-import '../src/fixAce.css';
-import DynamicTheme from './DynamicTheme';
-
-const onChangeAction = action('onChange');
-const onErrorAction = action('onError');
-
-let value = {
- this: 'this',
- is: 'is',
- 'JSON!!!111!!': 'JSON!!!111!!',
- 1: 1,
- 2: 1,
- 3: 1,
- 4: 1,
- 5: 1,
- 6: 1,
-};
-
-function handleChange(json) {
- onChangeAction(JSON.stringify(json));
- value = json;
-}
-
-function handleError(error) {
- onErrorAction(JSON.stringify(error));
-}
-
-const schema = {
- type: 'object',
- properties: {
- some: {
- type: 'integer'
- }
- },
- required: ['some']
-};
-
-storiesOf('JsonEditor/modes/code', module)
- .addDecorator(Decorator)
- .add('onChange', () => (
-
- ))
- .add('onError', () => (
-
- ))
- .add('customization', () => (
-
- ));
-
-const onEventAction = action('onEvent');
-function handleEvent(node, event) {
- onEventAction(JSON.stringify({ node, eventType: event.type }));
-}
-
-storiesOf('JsonEditor/modes/form', module)
- .addDecorator(Decorator)
- .add('with onEvent handler', () => (
-
- ))
- .add('with history enabled', () => (
-
- ));
-
-const submitAction = action('onSubmit');
-const form = reduxForm({
- form: 'form',
- initialValues: { field: value }
-})(() => (
-
-));
-
-storiesOf('JsonEditor/redux-form', module)
- .addDecorator(reduxDecorator)
- .add('controlling by redux-form', () => React.createElement(form));
-
-const aceStory = storiesOf('JsonEditor/ace', module).addDecorator(Decorator);
-
-const aceThemes = require.context('brace/theme/', false, /.js$/);
-
-aceThemes.keys().forEach((key) => {
- const themeName = key.replace('.js', '').replace(/^\./, '');
- aceStory.add(themeName, () => {
- aceThemes(key);
- return (
-
- );
- });
-});
-
-aceStory.add('Changing theme dynamically', () => (
-
-));
-
-storiesOf('JsonEditor/ajv', module)
- .addDecorator(Decorator)
- .add('validate', () => [
-
- Schema:
-
-
,
-
- JSON:
-
-
- ]);
+import React from 'react';
+import ace from 'brace';
+import 'brace/mode/json';
+import 'brace/theme/tomorrow_night_blue';
+import { Form, Field, reduxForm } from 'redux-form';
+import { storiesOf } from '@storybook/react';
+import { action } from '@storybook/addon-actions';
+import Ajv from 'ajv';
+import Editor from '../src/Editor';
+import Decorator from './decorator';
+import { reduxDecorator } from './reduxDecorator';
+import { FieldComponent } from './FieldComponent';
+
+import '../src/fixAce.css';
+import DynamicTheme from './DynamicTheme';
+
+const onChangeAction = action('onChange');
+const onErrorAction = action('onError');
+
+let value = {
+ this: 'this',
+ is: 'is',
+ 'JSON!!!111!!': 'JSON!!!111!!',
+ 1: 1,
+ 2: 1,
+ 3: 1,
+ 4: 1,
+ 5: 1,
+ 6: 1,
+};
+
+function handleChange(json) {
+ onChangeAction(JSON.stringify(json));
+ value = json;
+}
+
+function handleError(preJson, error) {
+ onErrorAction(error);
+ value = preJson;
+}
+
+const schema = {
+ type: 'object',
+ properties: {
+ some: {
+ type: 'integer'
+ }
+ },
+ required: ['some']
+};
+
+storiesOf('JsonEditor/modes/code', module)
+ .addDecorator(Decorator)
+ .add('onChange', () => (
+
+ ))
+ .add('onError', () => (
+
+ ))
+ .add('customization', () => (
+
+ ));
+
+const onEventAction = action('onEvent');
+function handleEvent(node, event) {
+ onEventAction(JSON.stringify({ node, eventType: event.type }));
+}
+
+storiesOf('JsonEditor/modes/form', module)
+ .addDecorator(Decorator)
+ .add('with onEvent handler', () => (
+
+ ))
+ .add('with history enabled', () => (
+
+ ));
+
+const submitAction = action('onSubmit');
+const form = reduxForm({
+ form: 'form',
+ initialValues: { field: value }
+})(() => (
+
+));
+
+storiesOf('JsonEditor/redux-form', module)
+ .addDecorator(reduxDecorator)
+ .add('controlling by redux-form', () => React.createElement(form));
+
+const aceStory = storiesOf('JsonEditor/ace', module).addDecorator(Decorator);
+
+const aceThemes = require.context('brace/theme/', false, /.js$/);
+
+aceThemes.keys().forEach((key) => {
+ const themeName = key.replace('.js', '').replace(/^\./, '');
+ aceStory.add(themeName, () => {
+ aceThemes(key);
+ return (
+
+ );
+ });
+});
+
+aceStory.add('Changing theme dynamically', () => (
+
+));
+
+storiesOf('JsonEditor/ajv', module)
+ .addDecorator(Decorator)
+ .add('validate', () => [
+
+ Schema:
+
+
,
+
+ JSON:
+
+
+ ]);
From 46433f38cadadf42f08749db562953025559148e Mon Sep 17 00:00:00 2001
From: xfause <919017553@qq.com>
Date: Fri, 20 Mar 2020 17:18:50 +0800
Subject: [PATCH 2/2] feat(onError): add handleError
add handleError
can get two params in onError
preJson: latest correct json
error: error from json.parse()
---
src/Editor.jsx | 539 ++++++++++++++++++++++++++-----------------------
1 file changed, 282 insertions(+), 257 deletions(-)
diff --git a/src/Editor.jsx b/src/Editor.jsx
index 9a1cc83..83d0a15 100644
--- a/src/Editor.jsx
+++ b/src/Editor.jsx
@@ -1,257 +1,282 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import JSONEditor from 'jsoneditor/dist/jsoneditor-minimalist';
-import 'jsoneditor/dist/jsoneditor.css';
-import './fixAce.css';
-
-/**
- * @typedef {{
- * tree: string,
- * view: string,
- * form: string,
- * code: string,
- * text: string,
- * allValues: Array
- * }} TJsonEditorModes
- */
-const modes = {
- tree: 'tree',
- view: 'view',
- form: 'form',
- code: 'code',
- text: 'text'
-};
-
-const values = Object.values(modes);
-
-modes.allValues = values;
-
-/**
- * @type {object}
- * @property {object} [value]
- * @property {string} [mode='tree'] - Set the editor mode.
- * @property {string} [name=undefined] - Initial field name for the root node
- * @property {object} [schema] - Validate the JSON object against a JSON schema.
- * @property {object} [schemaRefs] - Schemas that are referenced using
- * the $ref property
- * @property {Function} [onChange] - Set a callback function
- * triggered when the contents of the JSONEditor change.
- * Called without parameters. Will only be triggered on changes made by the user.
- * Return new json.
- * @property {Function} [onError] - Set a callback function triggered when an error occurs.
- * Invoked with the error as first argument.
- * The callback is only invoked for errors triggered by a users action,
- * like switching from code mode to tree mode or clicking
- * the Format button whilst the editor doesn't contain valid JSON.
- * @property {Function} [onModeChange] - Set a callback function
- * triggered right after the mode is changed by the user.
- * @property {object} [ace] - Provide a version of the Ace editor.
- * Only applicable when mode is code
- * @property {object} [ajv] - Provide a instance of ajv,
- * the library used for JSON schema validation.
- * @property {string} [theme] - Set the Ace editor theme,
- * uses included 'ace/theme/jsoneditor' by default.
- * @property {boolean} [history=false] - Enables history,
- * adds a button Undo and Redo to the menu of the JSONEditor. Only applicable when
- * mode is 'tree' or 'form'
- * @property {boolean} [navigationBar=true] - Adds navigation bar to the menu
- * the navigation bar visualize the current position on the
- * tree structure as well as allows breadcrumbs navigation.
- * @property {boolean} [statusBar=true] - Adds status bar to the buttom of the editor
- * the status bar shows the cursor position and a count of the selected characters.
- * Only applicable when mode is 'code' or 'text'.
- * @property {boolean} [search=true] - Enables a search box in
- * the upper right corner of the JSONEditor.
- * @property {Array} [allowedModes] - Create a box in the editor menu where
- * the user can switch between the specified modes.
- * @property {(string|PropTypes.elementType)} [tag='div'] - Html element, or react element to render
- * @property {object} [htmlElementProps] - html element custom props
- * @property {Function} [innerRef] - callback to get html element reference
- */
-export default class Editor extends Component {
- constructor(props) {
- super(props);
-
- this.htmlElementRef = null;
- this.jsonEditor = null;
-
- this.handleChange = this.handleChange.bind(this);
- this.setRef = this.setRef.bind(this);
- this.collapseAll = this.collapseAll.bind(this);
- this.expandAll = this.expandAll.bind(this);
- this.focus = this.focus.bind(this);
- }
-
- componentDidMount() {
- const {
- allowedModes,
- innerRef,
- htmlElementProps,
- tag,
- onChange,
- ...rest
- } = this.props;
-
- this.createEditor({
- ...rest,
- modes: allowedModes
- });
- }
-
- // eslint-disable-next-line react/sort-comp
- componentDidUpdate({
- allowedModes,
- schema,
- name,
- theme,
- schemaRefs,
- innerRef,
- htmlElementProps,
- tag,
- onChange,
- ...rest
- }) {
- if (this.jsonEditor) {
- if (theme !== this.props.theme) {
- this.createEditor({
- ...rest,
- theme,
- modes: allowedModes
- });
- } else {
- if (schema !== this.props.schema
- || schemaRefs !== this.props.schemaRefs
- ) {
- this.jsonEditor.setSchema(schema, schemaRefs);
- }
-
- if (name !== this.jsonEditor.getName()) {
- this.jsonEditor.setName(name);
- }
- }
- }
- }
-
- shouldComponentUpdate({ htmlElementProps }) {
- return htmlElementProps !== this.props.htmlElementProps;
- }
-
- componentWillUnmount() {
- if (this.jsonEditor) {
- this.jsonEditor.destroy();
- this.jsonEditor = null;
- }
- }
-
- setRef(element) {
- this.htmlElementRef = element;
- if (this.props.innerRef) {
- this.props.innerRef(element);
- }
- }
-
- createEditor({ value, ...rest }) {
- if (this.jsonEditor) {
- this.jsonEditor.destroy();
- }
-
- this.jsonEditor = new JSONEditor(this.htmlElementRef, {
- onChange: this.handleChange,
- ...rest
- });
-
- this.jsonEditor.set(value);
- }
-
- handleChange() {
- if (this.props.onChange) {
- try {
- const text = this.jsonEditor.getText();
- if (text === '') {
- this.props.onChange(null);
- }
-
- const currentJson = this.jsonEditor.get();
- if (this.props.value !== currentJson) {
- this.props.onChange(currentJson);
- }
- } catch (err) {
- this.err = err;
- }
- }
- }
-
- collapseAll() {
- if (this.jsonEditor) {
- this.jsonEditor.collapseAll();
- }
- }
-
- expandAll() {
- if (this.jsonEditor) {
- this.jsonEditor.expandAll();
- }
- }
-
- focus() {
- if (this.jsonEditor) {
- this.jsonEditor.focus();
- }
- }
-
- render() {
- const {
- htmlElementProps,
- tag
- } = this.props;
-
- return React.createElement(
- tag,
- {
- ...htmlElementProps,
- ref: this.setRef
- }
- );
- }
-}
-
-Editor.propTypes = {
- // jsoneditor props
- value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
- mode: PropTypes.oneOf(values),
- name: PropTypes.string,
- schema: PropTypes.object,
- schemaRefs: PropTypes.object,
-
- onChange: PropTypes.func,
- onError: PropTypes.func,
- onModeChange: PropTypes.func,
-
- ace: PropTypes.object,
- ajv: PropTypes.object,
- theme: PropTypes.string,
- history: PropTypes.bool,
- navigationBar: PropTypes.bool,
- statusBar: PropTypes.bool,
- search: PropTypes.bool,
- allowedModes: PropTypes.arrayOf(PropTypes.oneOf(values)),
-
- // custom props
- tag: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
- htmlElementProps: PropTypes.object,
- innerRef: PropTypes.func,
-};
-
-Editor.defaultProps = {
- tag: 'div',
- mode: modes.tree,
- history: false,
- search: true,
- navigationBar: true,
- statusBar: true,
-};
-
-/**
- * @type TJsonEditorModes
- */
-Editor.modes = modes;
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import JSONEditor from 'jsoneditor/dist/jsoneditor-minimalist';
+import 'jsoneditor/dist/jsoneditor.css';
+import './fixAce.css';
+
+/**
+ * @typedef {{
+ * tree: string,
+ * view: string,
+ * form: string,
+ * code: string,
+ * text: string,
+ * allValues: Array
+ * }} TJsonEditorModes
+ */
+const modes = {
+ tree: 'tree',
+ view: 'view',
+ form: 'form',
+ code: 'code',
+ text: 'text'
+};
+
+const values = Object.values(modes);
+
+modes.allValues = values;
+
+/**
+ * @type {object}
+ * @property {object} [value]
+ * @property {string} [mode='tree'] - Set the editor mode.
+ * @property {string} [name=undefined] - Initial field name for the root node
+ * @property {object} [schema] - Validate the JSON object against a JSON schema.
+ * @property {object} [schemaRefs] - Schemas that are referenced using
+ * the $ref property
+ * @property {Function} [onChange] - Set a callback function
+ * triggered when the contents of the JSONEditor change.
+ * Called without parameters. Will only be triggered on changes made by the user.
+ * Return new json.
+ * @property {Function} [onError] - Set a callback function triggered when an error occurs.
+ * Invoked with the error as first argument.
+ * The callback is only invoked for errors triggered by a users action,
+ * like switching from code mode to tree mode or clicking
+ * the Format button whilst the editor doesn't contain valid JSON.
+ * @property {Function} [onModeChange] - Set a callback function
+ * triggered right after the mode is changed by the user.
+ * @property {object} [ace] - Provide a version of the Ace editor.
+ * Only applicable when mode is code
+ * @property {object} [ajv] - Provide a instance of ajv,
+ * the library used for JSON schema validation.
+ * @property {string} [theme] - Set the Ace editor theme,
+ * uses included 'ace/theme/jsoneditor' by default.
+ * @property {boolean} [history=false] - Enables history,
+ * adds a button Undo and Redo to the menu of the JSONEditor. Only applicable when
+ * mode is 'tree' or 'form'
+ * @property {boolean} [navigationBar=true] - Adds navigation bar to the menu
+ * the navigation bar visualize the current position on the
+ * tree structure as well as allows breadcrumbs navigation.
+ * @property {boolean} [statusBar=true] - Adds status bar to the buttom of the editor
+ * the status bar shows the cursor position and a count of the selected characters.
+ * Only applicable when mode is 'code' or 'text'.
+ * @property {boolean} [search=true] - Enables a search box in
+ * the upper right corner of the JSONEditor.
+ * @property {Array} [allowedModes] - Create a box in the editor menu where
+ * the user can switch between the specified modes.
+ * @property {(string|PropTypes.elementType)} [tag='div'] - Html element, or react element to render
+ * @property {object} [htmlElementProps] - html element custom props
+ * @property {Function} [innerRef] - callback to get html element reference
+ */
+export default class Editor extends Component {
+ constructor(props) {
+ super(props);
+
+ this.htmlElementRef = null;
+ this.jsonEditor = null;
+
+ this.handleChange = this.handleChange.bind(this);
+ this.handleError = this.handleError.bind(this);
+ this.setRef = this.setRef.bind(this);
+ this.collapseAll = this.collapseAll.bind(this);
+ this.expandAll = this.expandAll.bind(this);
+ this.focus = this.focus.bind(this);
+ this.state = {
+ previousJson: this.props.value,
+ };
+ }
+
+ componentDidMount() {
+ const {
+ allowedModes,
+ innerRef,
+ htmlElementProps,
+ tag,
+ onChange,
+ onError,
+ ...rest
+ } = this.props;
+
+ this.createEditor({
+ ...rest,
+ modes: allowedModes
+ });
+ }
+
+ // eslint-disable-next-line react/sort-comp
+ componentDidUpdate({
+ allowedModes,
+ schema,
+ name,
+ theme,
+ schemaRefs,
+ innerRef,
+ htmlElementProps,
+ tag,
+ onChange,
+ onError,
+ ...rest
+ }) {
+ if (this.jsonEditor) {
+ if (theme !== this.props.theme) {
+ this.createEditor({
+ ...rest,
+ theme,
+ modes: allowedModes
+ });
+ } else {
+ if (schema !== this.props.schema
+ || schemaRefs !== this.props.schemaRefs
+ ) {
+ this.jsonEditor.setSchema(schema, schemaRefs);
+ }
+
+ if (name !== this.jsonEditor.getName()) {
+ this.jsonEditor.setName(name);
+ }
+ }
+ }
+ }
+
+ shouldComponentUpdate({ htmlElementProps }) {
+ return htmlElementProps !== this.props.htmlElementProps;
+ }
+
+ componentWillUnmount() {
+ if (this.jsonEditor) {
+ this.jsonEditor.destroy();
+ this.jsonEditor = null;
+ }
+ }
+
+ setRef(element) {
+ this.htmlElementRef = element;
+ if (this.props.innerRef) {
+ this.props.innerRef(element);
+ }
+ }
+
+ createEditor({ value, ...rest }) {
+ if (this.jsonEditor) {
+ this.jsonEditor.destroy();
+ }
+
+ this.jsonEditor = new JSONEditor(this.htmlElementRef, {
+ onChange: this.handleChange,
+ onError: this.handleError,
+ ...rest
+ });
+
+ this.jsonEditor.set(value);
+ }
+
+ handleChange() {
+ if (this.props.onChange) {
+ try {
+ const text = this.jsonEditor.getText();
+ if (text === '') {
+ this.props.onChange(null);
+ }
+
+ const currentJson = this.jsonEditor.get();
+ if (this.props.value !== currentJson) {
+ this.props.onChange(currentJson);
+ }
+ } catch (err) {
+ this.err = err;
+ }
+ }
+ if (this.props.onError) {
+ this.handleError();
+ }
+ }
+
+ handleError() {
+ if (this.props.onError) {
+ try {
+ const text = this.jsonEditor.getText();
+ const pj = JSON.parse(text);
+ this.setState({
+ previousJson: pj
+ });
+ } catch (err) {
+ this.props.onError(this.state.previousJson, err);
+ this.err = err;
+ }
+ }
+ }
+
+ collapseAll() {
+ if (this.jsonEditor) {
+ this.jsonEditor.collapseAll();
+ }
+ }
+
+ expandAll() {
+ if (this.jsonEditor) {
+ this.jsonEditor.expandAll();
+ }
+ }
+
+ focus() {
+ if (this.jsonEditor) {
+ this.jsonEditor.focus();
+ }
+ }
+
+ render() {
+ const {
+ htmlElementProps,
+ tag
+ } = this.props;
+
+ return React.createElement(
+ tag,
+ {
+ ...htmlElementProps,
+ ref: this.setRef
+ }
+ );
+ }
+}
+
+Editor.propTypes = {
+ // jsoneditor props
+ value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
+ mode: PropTypes.oneOf(values),
+ name: PropTypes.string,
+ schema: PropTypes.object,
+ schemaRefs: PropTypes.object,
+
+ onChange: PropTypes.func,
+ onError: PropTypes.func,
+ onModeChange: PropTypes.func,
+
+ ace: PropTypes.object,
+ ajv: PropTypes.object,
+ theme: PropTypes.string,
+ history: PropTypes.bool,
+ navigationBar: PropTypes.bool,
+ statusBar: PropTypes.bool,
+ search: PropTypes.bool,
+ allowedModes: PropTypes.arrayOf(PropTypes.oneOf(values)),
+
+ // custom props
+ tag: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
+ htmlElementProps: PropTypes.object,
+ innerRef: PropTypes.func,
+};
+
+Editor.defaultProps = {
+ tag: 'div',
+ mode: modes.tree,
+ history: false,
+ search: true,
+ navigationBar: true,
+ statusBar: true,
+};
+
+/**
+ * @type TJsonEditorModes
+ */
+Editor.modes = modes;