You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by be...@apache.org on 2015/06/16 22:16:10 UTC
fauxton commit: updated refs/heads/master to f5f375c
Repository: couchdb-fauxton
Updated Branches:
refs/heads/master 9d7cd4421 -> f5f375c59
Refactoring <CodeEditor />
This PR refactors the <CodeEditor /> component to make it
more generic. Ace editor instances such as the mango, index
editor, and Zen mode now use this via wrapper components
(<CodeEditorPanel /> and <ZenModeOverlay />). The goal is
to make this the one any only place where the ace editor
is instantiated. It'll also be needed for the next step:
refactoring the full page doc editor.
Also: Removed extra code to clear notifications in
<Editor />. This was redundant since the "there have been
changed" popup doesn't (and shouldn't, I'd argue) appear
for small editors.
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/f5f375c5
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/f5f375c5
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/f5f375c5
Branch: refs/heads/master
Commit: f5f375c59c2f43577a3457b2652bc0d066cc1466
Parents: 9d7cd44
Author: Ben Keen <be...@gmail.com>
Authored: Wed Jun 10 11:41:06 2015 -0700
Committer: Ben Keen <be...@gmail.com>
Committed: Tue Jun 16 12:36:36 2015 -0700
----------------------------------------------------------------------
.../components/react-components.react.jsx | 446 +++++++++++--------
.../tests/codeEditorPanelSpec.react.jsx | 66 +++
app/addons/components/tests/codeEditorSpec.js | 134 ------
.../components/tests/codeEditorSpec.react.jsx | 103 +----
.../components/tests/zenModeSpec.react.jsx | 5 +-
.../documents/index-editor/components.react.jsx | 31 +-
.../tests/viewIndex.componentsSpec.react.jsx | 6 +-
.../documents/mango/mango.components.react.jsx | 11 +-
8 files changed, 359 insertions(+), 443 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/components/react-components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/react-components.react.jsx b/app/addons/components/react-components.react.jsx
index d543a09..1186c83 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -64,6 +64,132 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
}
});
+
+ /**
+ * A pre-packaged JS editor panel for use on the Edit Index / Mango pages. Includes options for a title, zen mode
+ * icon and beautify button.
+ */
+ var CodeEditorPanel = React.createClass({
+ getDefaultProps: function () {
+ return {
+ id: 'code-editor',
+ defaultCode: '',
+ title: '',
+ docLink: '',
+ allowZenMode: true,
+ blur: function () {}
+ };
+ },
+
+ getInitialState: function () {
+ return this.getStoreState();
+ },
+
+ getStoreState: function () {
+ return {
+ zenModeEnabled: false,
+ code: this.props.defaultCode
+ };
+ },
+
+ componentWillReceiveProps: function (nextProps) {
+ if (nextProps.defaultCode !== this.props.defaultCode) {
+ this.setState({ code: nextProps.defaultCode });
+ }
+ },
+
+ // list of JSHINT errors to ignore: gets around problem of anonymous functions not being valid
+ ignorableErrors: [
+ 'Missing name in function declaration.',
+ "['{a}'] is better written in dot notation."
+ ],
+
+ getZenModeIcon: function () {
+ if (this.props.allowZenMode) {
+ return <span className="fonticon fonticon-resize-full zen-editor-icon" title="Enter Zen mode" onClick={this.enterZenMode}></span>;
+ }
+ },
+
+ getDocIcon: function () {
+ if (this.props.docLink) {
+ return (
+ <a className="help-link"
+ data-bypass="true"
+ href={this.props.docLink}
+ target="_blank"
+ >
+ <i className="icon-question-sign"></i>
+ </a>
+ );
+ }
+ },
+
+ getZenModeOverlay: function () {
+ if (this.state.zenModeEnabled) {
+ return (
+ <ZenModeOverlay
+ defaultCode={this.state.code}
+ mode={this.props.mode}
+ ignorableErrors={this.ignorableErrors}
+ onExit={this.exitZenMode}
+ />
+ );
+ }
+ },
+
+ enterZenMode: function () {
+ this.setState({
+ zenModeEnabled: true,
+ code: this.refs.codeEditor.getValue()
+ });
+ },
+
+ exitZenMode: function (content) {
+ this.setState({
+ zenModeEnabled: false,
+ code: content
+ });
+ },
+
+ getEditor: function () {
+ return this.refs.codeEditor;
+ },
+
+ getValue: function () {
+ return this.getEditor().getValue();
+ },
+
+ beautify: function (code) {
+ this.setState({ code: code });
+ },
+
+ render: function () {
+ return (
+ <div className="control-group">
+ <label>
+ <strong>{this.props.title + ' '}</strong>
+ {this.getDocIcon()}
+ {this.getZenModeIcon()}
+ </label>
+ <CodeEditor
+ id={this.props.id}
+ ref="codeEditor"
+ mode="javascript"
+ defaultCode={this.state.code}
+ showGutter={true}
+ ignorableErrors={this.ignorableErrors}
+ setHeightToLineCount={true}
+ blur={this.props.blur}
+ />
+ <Beautify code={this.state.code} beautifiedCode={this.beautify} />
+ {this.getZenModeOverlay()}
+ </div>
+ );
+ }
+ });
+
+
+ // a generic Ace Editor component. This should be the only place in the app that instantiates an editor
var CodeEditor = React.createClass({
getDefaultProps: function () {
return {
@@ -71,82 +197,106 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
mode: 'javascript',
theme: 'idle_fingers',
fontSize: 13,
- code: '',
- showEditorOnly: false,
+ defaultCode: '',
showGutter: true,
highlightActiveLine: true,
showPrintMargin: false,
autoScrollEditorIntoView: true,
- setHeightWithJS: true,
- isFullPageEditor: false,
- disableUnload: false,
- allowZenMode: true,
- allowAnonFunction: true, // allows JS fragments, like `function (doc) { emit(doc._id, 1); `}. Suppresses JS errors
- change: function () {}
+ autoFocus: false,
+
+ // these two options create auto-resizeable code editors, with a maximum number of lines
+ setHeightToLineCount: false,
+ maxLines: 10,
+
+ // optional editor key commands (e.g. specific save action)
+ editorCommands: [],
+
+ // notifies users that there is unsaved changes in the editor when navigating away from the page
+ notifyUnsavedChanges: false,
+
+ // an optional array of ignorable Ace editors. Lets us filter out errors based on context
+ ignorableErrors: [],
+
+ // un-Reacty, but the code editor is a self-contained component and it's helpful to be able to tie into
+ // editor specific events like content changes and leaving the editor
+ change: function () {},
+ blur: function () {}
};
},
+ // used purely to keep track of content changes. This is only reset via an explicit clearChanges() call
getInitialState: function () {
- return this.getStoreState();
- },
-
- getStoreState: function () {
return {
- zenModeEnabled: false
+ originalCode: this.props.defaultCode
};
},
hasChanged: function () {
- return !_.isEqual(this.props.code, this.getValue());
+ return !_.isEqual(this.state.originalCode, this.getValue());
},
- setupAce: function (props, shouldUpdateCode) {
- var el = this.refs.ace.getDOMNode();
+ clearChanges: function () {
+ this.setState({
+ originalCode: this.getValue()
+ });
+ },
- //set the id so our nightwatch tests can find it
- el.id = props.id;
+ setupAce: function (props, shouldUpdateCode) {
+ this.editor = ace.edit(this.refs.ace.getDOMNode());
- this.editor = ace.edit(el);
- // Automatically scrolling cursor into view after selection
- // change this will be disabled in the next version
- // set editor.$blockScrolling = Infinity to disable this message
+ // suppresses an Ace editor error
this.editor.$blockScrolling = Infinity;
if (shouldUpdateCode) {
- this.setEditorValue(props.code);
+ this.setEditorValue(props.defaultCode);
}
this.editor.setShowPrintMargin(props.showPrintMargin);
this.editor.autoScrollEditorIntoView = props.autoScrollEditorIntoView;
this.editor.setOption('highlightActiveLine', this.props.highlightActiveLine);
- this.setHeightToLineCount();
- if (this.props.allowAnonFunction) {
- this.removeIncorrectAnnotations(this.editor);
+ if (this.props.setHeightToLineCount) {
+ this.setHeightToLineCount();
+ }
+
+ if (this.props.ignorableErrors) {
+ this.removeIgnorableAnnotations();
}
- this.editor.getSession().setMode("ace/mode/" + props.mode);
- this.editor.setTheme("ace/theme/" + props.theme);
+ this.addCommands();
+ this.editor.getSession().setMode('ace/mode/' + props.mode);
+ this.editor.setTheme('ace/theme/' + props.theme);
this.editor.setFontSize(props.fontSize);
this.editor.getSession().setUseSoftTabs(true);
+
+ if (this.props.autoFocus) {
+ this.editor.focus();
+ }
},
- onChange: function () {
- this.setState(this.getStoreState());
+ addCommands: function () {
+ _.each(this.props.editorCommands, function (command) {
+ this.editor.commands.addCommand(command);
+ }, this);
},
setupEvents: function () {
- this.editor.on('blur', _.bind(this.saveCodeChange, this));
-
- if (this.props.disableUnload) {
- return;
+ this.editor.on('blur', _.bind(this.onBlur, this));
+ this.editor.on('change', _.bind(this.onContentChange, this));
+ if (this.props.notifyUnsavedChanges) {
+ $(window).on('beforeunload.editor_' + this.props.id, _.bind(this.quitWarningMsg));
+ FauxtonAPI.beforeUnload('editor_' + this.props.id, _.bind(this.quitWarningMsg, this));
}
+ },
- $(window).on('beforeunload.editor_' + this.props.id, _.bind(this.quitWarningMsg));
- FauxtonAPI.beforeUnload('editor_' + this.props.id, _.bind(this.quitWarningMsg, this));
+ onBlur: function () {
+ this.props.blur(this.getValue());
},
- saveCodeChange: function () {
+ onContentChange: function () {
+ if (this.props.setHeightToLineCount) {
+ this.setHeightToLineCount();
+ }
this.props.change(this.getValue());
},
@@ -157,55 +307,17 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
},
removeEvents: function () {
- if (this.props.disableUnload) {
- return;
+ if (this.props.notifyUnsavedChanges) {
+ $(window).off('beforeunload.editor_' + this.props.id);
+ FauxtonAPI.removeBeforeUnload('editor_' + this.props.id);
}
-
- $(window).off('beforeunload.editor_' + this.props.id);
- FauxtonAPI.removeBeforeUnload('editor_' + this.props.id);
},
setHeightToLineCount: function () {
- if (!this.props.setHeightWithJS) {
- return;
- }
-
- var lines = this.editor.getSession().getDocument().getLength();
-
- if (this.props.isFullPageEditor) {
- var maxLines = this.getMaxAvailableLinesOnPage();
- lines = lines < maxLines ? lines : maxLines;
- }
+ var numLines = this.editor.getSession().getDocument().getLength();
+ var maxLines = (numLines > this.props.maxLines) ? this.props.maxLines : numLines;
this.editor.setOptions({
- maxLines: lines
- });
- },
-
- // List of JSHINT errors to ignore
- // Gets around problem of anonymous functions not being a valid statement
- excludedViewErrors: [
- "Missing name in function declaration.",
- "['{a}'] is better written in dot notation."
- ],
-
- isIgnorableError: function (msg) {
- return _.contains(this.excludedViewErrors, msg);
- },
-
- removeIncorrectAnnotations: function (editor) {
- var isIgnorableError = this.isIgnorableError;
- editor.getSession().on("changeAnnotation", function () {
- var annotations = editor.getSession().getAnnotations();
- var newAnnotations = _.reduce(annotations, function (annotations, error) {
- if (!isIgnorableError(error.raw)) {
- annotations.push(error);
- }
- return annotations;
- }, []);
-
- if (annotations.length !== newAnnotations.length) {
- editor.getSession().setAnnotations(newAnnotations);
- }
+ maxLines: maxLines
});
},
@@ -220,56 +332,45 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
},
componentWillReceiveProps: function (nextProps) {
- var codeChanged = !_.isEqual(nextProps.code, this.getValue());
+ var codeChanged = !_.isEqual(nextProps.defaultCode, this.getValue());
this.setupAce(nextProps, codeChanged);
},
- editSaved: function () {
- return this.hasChanged();
- },
-
- zenModeIcon: function () {
- if (this.props.allowZenMode) {
- return <span className="fonticon fonticon-resize-full zen-editor-icon" title="Enter Zen mode" onClick={this.enterZenMode}></span>;
- }
+ getAnnotations: function () {
+ return this.editor.getSession().getAnnotations();
},
- enterZenMode: function () {
- this.setState({ zenModeEnabled: true });
+ isIgnorableError: function (msg) {
+ return _.contains(this.props.ignorableErrors, msg);
},
- getTitleFragment: function () {
- if (!this.props.docs) {
- return (<strong>{this.props.title}</strong>);
- }
-
- return (
- <label>
- <strong>{this.props.title + ' '}</strong>
- <a
- className="help-link"
- data-bypass="true"
- href={this.props.docs}
- target="_blank"
- >
- <i className="icon-question-sign"></i>
- </a>
- {this.zenModeIcon()}
- </label>
- );
- },
+ removeIgnorableAnnotations: function () {
+ var isIgnorableError = this.isIgnorableError;
+ this.editor.getSession().on('changeAnnotation', function () {
+ var annotations = this.editor.getSession().getAnnotations();
+ var newAnnotations = _.reduce(annotations, function (annotations, error) {
+ if (!isIgnorableError(error.raw)) {
+ annotations.push(error);
+ }
+ return annotations;
+ }, []);
- getAnnotations: function () {
- return this.editor.getSession().getAnnotations();
+ if (annotations.length !== newAnnotations.length) {
+ this.editor.getSession().setAnnotations(newAnnotations);
+ }
+ }.bind(this));
},
+ // ------------------
+ // TODO two things to do after full page doc editor refactor:
+ // 1. rename to hasErrors()
hadValidCode: function () {
var errors = this.getAnnotations();
- // By default CouchDB view functions don't pass lint
return _.every(errors, function (error) {
return this.isIgnorableError(error.raw);
}, this);
},
+ // ------------------
setEditorValue: function (code, lineNumber) {
lineNumber = lineNumber ? lineNumber : -1;
@@ -280,41 +381,9 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
return this.editor.getValue();
},
- getEditor: function () {
- return this;
- },
-
- getZenModeOverlay: function () {
- if (this.state.zenModeEnabled) {
- return (
- <ZenModeOverlay
- visible={this.state.zenModeEnabled}
- defaultCode={this.getValue()}
- mode={this.props.mode}
- removeIncorrectAnnotations={this.removeIncorrectAnnotations}
- onExit={this.onExitZenMode}
- />
- );
- }
- },
-
- onExitZenMode: function (content) {
- this.setEditorValue(content);
- this.setState({ zenModeEnabled: false });
- },
-
render: function () {
- if (this.props.showEditorOnly) {
- return (<div ref="ace" className="js-editor" id={this.props.id}></div>);
- }
-
return (
- <div className="control-group">
- {this.getTitleFragment()}
- <div ref="ace" className="js-editor" id={this.props.id}></div>
- <Beautify code={this.props.code} beautifiedCode={this.setEditorValue} />
- {this.getZenModeOverlay()}
- </div>
+ <div ref="ace" className="js-editor" id={this.props.id}></div>
);
}
});
@@ -330,12 +399,17 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
return {
mode: 'javascript',
defaultCode: '',
- removeIncorrectAnnotations: null,
+ ignorableErrors: [],
onExit: null,
highlightActiveLine: false
};
},
+ themes: {
+ dark: 'idle_fingers',
+ light: 'dawn'
+ },
+
getInitialState: function () {
return this.getStoreState();
},
@@ -357,64 +431,41 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
},
componentDidMount: function () {
- this.update();
- this.editor.focus();
$(this.refs.exit.getDOMNode()).tooltip({ placement: 'left' });
$(this.refs.theme.getDOMNode()).tooltip({ placement: 'left' });
},
- componentDidUpdate: function () {
- this.update();
+ exitZenMode: function () {
+ this.props.onExit(this.getValue());
},
- exitZenMode: function () {
- this.props.onExit(this.editor.getValue());
+ getValue: function () {
+ return this.refs.ace.getValue();
},
toggleTheme: function () {
var newTheme = (this.state.theme === 'dark') ? 'light' : 'dark';
this.setState({
theme: newTheme,
- code: this.editor.getValue()
+ code: this.getValue()
});
app.utils.localStorageSet('zenTheme', newTheme);
},
- update: function () {
- var el = this.refs.ace.getDOMNode();
- this.editor = ace.edit(el);
- this.editor.$blockScrolling = Infinity;
- this.setEditorValue(this.state.code);
-
- var theme = this.state.theme === 'dark' ? 'idle_fingers' : 'dawn';
- this.editor.setTheme('ace/theme/' + theme);
- this.editor.getSession().setMode('ace/mode/' + this.props.mode);
- this.editor.getSession().setUseSoftTabs(true);
- this.editor.setOption('highlightActiveLine', this.props.highlightActiveLine);
- this.editor.setShowPrintMargin(false);
-
- // escape exits zen mode. Add the key binding
- this.editor.commands.addCommand({
- name: "close",
- bindKey: { win: "ESC", mac: "ESC" },
- exec: function () {
- this.exitZenMode();
- }.bind(this)
- });
-
- // if an annotation removal method has been passed, ensure it's called so that error messages are cleaned
- if (this.props.removeIncorrectAnnotations) {
- this.props.removeIncorrectAnnotations(this.editor);
- }
- },
-
setEditorValue: function (code, lineNumber) {
lineNumber = lineNumber ? lineNumber : -1;
this.editor.setValue(code, lineNumber);
},
render: function () {
- var classes = "full-page-editor-modal-wrapper zen-theme-" + this.state.theme;
+ var classes = 'full-page-editor-modal-wrapper zen-theme-' + this.state.theme;
+
+ var editorCommands = [{
+ name: 'close',
+ bindKey: { win: 'ESC', mac: 'ESC' },
+ exec: this.exitZenMode
+ }];
+
return (
<div className={classes}>
<div className="zen-mode-controls">
@@ -438,7 +489,15 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
</ul>
<div className="tooltips"></div>
</div>
- <div ref="ace" className="js-editor"></div>
+ <CodeEditor
+ ref="ace"
+ autoFocus={true}
+ theme={this.themes[this.state.theme]}
+ defaultCode={this.props.defaultCode}
+ editorCommands={editorCommands}
+ ignorableErrors={this.props.ignorableErrors}
+ highlightActiveLine={this.props.highlightActiveLine}
+ />
</div>
);
}
@@ -503,7 +562,6 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
});
var Document = React.createClass({
-
propTypes: {
docIdentifier: React.PropTypes.string.isRequired,
docChecked: React.PropTypes.func.isRequired
@@ -534,7 +592,6 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
},
getCheckbox: function () {
-
if (!this.props.isDeletable) {
return <div className="checkbox-dummy"></div>;
}
@@ -594,9 +651,7 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
});
var LoadLines = React.createClass({
-
render: function () {
-
return (
<div className="loading-lines">
<div id="line1"> </div>
@@ -606,7 +661,6 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
</div>
);
}
-
});
var ConfirmButton = React.createClass({
@@ -660,8 +714,8 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
<ul className="dropdown-menu arrow" key={key} role="menu" aria-labelledby="dLabel">
{this.createSectionTitle(linkSection.title)}
{this.createSectionLinks(linkSection.links)}
- </ul>
- );
+ </ul>
+ );
}.bind(this));
},
@@ -676,10 +730,12 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
});
- var ReactComponents = {
+
+ return {
ConfirmButton: ConfirmButton,
ToggleHeaderButton: ToggleHeaderButton,
StyledSelect: StyledSelect,
+ CodeEditorPanel: CodeEditorPanel,
CodeEditor: CodeEditor,
ZenModeOverlay: ZenModeOverlay,
Beautify: Beautify,
@@ -689,6 +745,4 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
MenuDropDown: MenuDropDown
};
- return ReactComponents;
-
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/components/tests/codeEditorPanelSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/codeEditorPanelSpec.react.jsx b/app/addons/components/tests/codeEditorPanelSpec.react.jsx
new file mode 100644
index 0000000..b0bbcce
--- /dev/null
+++ b/app/addons/components/tests/codeEditorPanelSpec.react.jsx
@@ -0,0 +1,66 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+ 'api',
+ 'addons/components/react-components.react',
+ 'testUtils',
+ 'react'
+], function (FauxtonAPI, ReactComponents, utils, React) {
+
+ var assert = utils.assert;
+ var TestUtils = React.addons.TestUtils;
+ var code = 'function (doc) {\n emit(doc._id, 1);\n}';
+
+ describe('CodeEditorPanel', function () {
+
+ describe('Doc icon', function () {
+ it('hidden by default', function () {
+ var container = document.createElement('div');
+ var codeEditorEl = TestUtils.renderIntoDocument(
+ <ReactComponents.CodeEditorPanel defaultCode={code} />,
+ container
+ );
+ assert.equal($(codeEditorEl.getDOMNode()).find('.icon-question-sign').length, 0);
+ });
+ it('hidden by default', function () {
+ var container = document.createElement('div');
+ var codeEditorEl = TestUtils.renderIntoDocument(
+ <ReactComponents.CodeEditorPanel defaultCode={code} docLink="http://link.com" />,
+ container
+ );
+ assert.equal($(codeEditorEl.getDOMNode()).find('.icon-question-sign').length, 1);
+ });
+ });
+
+ describe('Zen Mode', function () {
+ it('shows zen mode by default', function () {
+ var container = document.createElement('div');
+ var codeEditorEl = TestUtils.renderIntoDocument(
+ <ReactComponents.CodeEditorPanel defaultCode={code} />,
+ container
+ );
+ assert.equal($(codeEditorEl.getDOMNode()).find('.zen-editor-icon').length, 1);
+ });
+
+ it('omits zen mode if explicitly turned off', function () {
+ var container = document.createElement('div');
+ var codeEditorEl = TestUtils.renderIntoDocument(
+ <ReactComponents.CodeEditor defaultCode={code} allowZenMode={false} />,
+ container
+ );
+ assert.equal($(codeEditorEl.getDOMNode()).find('.zen-editor-icon').length, 0);
+ });
+ });
+
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/components/tests/codeEditorSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/codeEditorSpec.js b/app/addons/components/tests/codeEditorSpec.js
deleted file mode 100644
index 3832cd4..0000000
--- a/app/addons/components/tests/codeEditorSpec.js
+++ /dev/null
@@ -1,134 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-define([
- 'api',
- 'addons/components/react-components.react',
-
- 'testUtils',
- 'react'
-], function (FauxtonAPI, ReactComponents, utils, React) {
-
- var assert = utils.assert;
- var TestUtils = React.addons.TestUtils;
- var code = 'function (doc) {\n emit(doc._id, 1);\n}';
- var code2 = 'function (doc) {\n if(doc._id) { \n emit(doc._id, 2); \n } \n}';
-
- describe('Code Editor', function () {
- var container, codeEditorEl, spy;
-
- beforeEach(function () {
- spy = sinon.spy();
- container = document.createElement('div');
- codeEditorEl = TestUtils.renderIntoDocument(
- React.createElement(ReactComponents.CodeEditor, {code: code, change: spy}),
- container
- );
- });
-
- afterEach(function () {
- React.unmountComponentAtNode(container);
- });
-
- describe('Tracking edits', function () {
-
- it('no change on mount', function () {
- assert.notOk(codeEditorEl.hasChanged());
- });
-
- it('detects change on user input', function () {
- codeEditorEl.editor.setValue(code2, -1);
-
- assert.ok(codeEditorEl.hasChanged());
- });
-
- });
-
- describe('onBlur', function () {
-
- it('calls changed function', function () {
- codeEditorEl.editor._emit('blur');
- assert.ok(spy.calledOnce);
- });
-
- });
-
- describe('setHeightToLineCount', function () {
-
- beforeEach(function () {
- codeEditorEl = TestUtils.renderIntoDocument(
- React.createElement(ReactComponents.CodeEditor, {code: code, isFullPageEditor: false, setHeightWithJS: true}),
- container
- );
-
- });
-
- it('sets line height correctly for non full page', function () {
- var spy = sinon.spy(codeEditorEl.editor, 'setOptions');
-
- codeEditorEl.setHeightToLineCount();
- assert.ok(spy.calledOnce);
- assert.equal(spy.getCall(0).args[0].maxLines, 3);
- });
-
- });
-
- describe('removeIncorrectAnnotations', function () {
-
- beforeEach(function () {
- codeEditorEl = TestUtils.renderIntoDocument(
- React.createElement(ReactComponents.CodeEditor, {code: code}),
- container
- );
-
- });
-
- it('removes default errors that do not apply to CouchDB Views', function () {
- assert.equal(codeEditorEl.getAnnotations(), 0);
- });
-
- });
-
- describe('setEditorValue', function () {
-
- it('sets new code', function () {
- codeEditorEl = TestUtils.renderIntoDocument(
- React.createElement(ReactComponents.CodeEditor, {code: code}),
- container
- );
-
- codeEditorEl.setEditorValue(code2);
- assert.deepEqual(codeEditorEl.getValue(), code2);
- });
-
- });
-
- describe('showEditorOnly', function () {
-
- it('only shows editor when showEditorOnly=true', function () {
- codeEditorEl = TestUtils.renderIntoDocument(
- React.createElement(ReactComponents.CodeEditor, {code: code, showEditorOnly: true}),
- container
- );
- assert.notOk($(codeEditorEl.getDOMNode()).hasClass('control-group'));
- });
-
- it('shows everything by default', function () {
- var codeEditorEl = TestUtils.renderIntoDocument(
- React.createElement(ReactComponents.CodeEditor, {code: code}),
- container
- );
- assert.ok($(codeEditorEl.getDOMNode()).hasClass('control-group'));
- });
-
- });
- });
-});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/components/tests/codeEditorSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/codeEditorSpec.react.jsx b/app/addons/components/tests/codeEditorSpec.react.jsx
index 3d77c89..4f1fcaf 100644
--- a/app/addons/components/tests/codeEditorSpec.react.jsx
+++ b/app/addons/components/tests/codeEditorSpec.react.jsx
@@ -21,6 +21,11 @@ define([
var code = 'function (doc) {\n emit(doc._id, 1);\n}';
var code2 = 'function (doc) {\n if(doc._id) { \n emit(doc._id, 2); \n } \n}';
+ var ignorableErrors = [
+ 'Missing name in function declaration.',
+ "['{a}'] is better written in dot notation."
+ ];
+
describe('Code Editor', function () {
var container, codeEditorEl, spy;
@@ -28,7 +33,7 @@ define([
spy = sinon.spy();
container = document.createElement('div');
codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code} change={spy} />,
+ <ReactComponents.CodeEditor defaultCode={code} blur={spy} />,
container
);
});
@@ -38,124 +43,58 @@ define([
});
describe('Tracking edits', function () {
-
it('no change on mount', function () {
assert.notOk(codeEditorEl.hasChanged());
});
it('detects change on user input', function () {
codeEditorEl.editor.setValue(code2, -1);
-
assert.ok(codeEditorEl.hasChanged());
});
-
});
describe('onBlur', function () {
-
- it('calls changed function', function () {
+ it('calls blur function', function () {
codeEditorEl.editor._emit('blur');
assert.ok(spy.calledOnce);
});
-
});
describe('setHeightToLineCount', function () {
-
- beforeEach(function () {
+ it('check default num lines #1', function () {
codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code} isFullPageEditor={false} setHeightWithJS={true}/>,
+ <ReactComponents.CodeEditor code={code} setHeightToLineCount={true} />,
container
);
-
- });
-
- it('sets line height correctly for non full page', function () {
- var spy = sinon.spy(codeEditorEl.editor, 'setOptions');
-
- codeEditorEl.setHeightToLineCount();
- assert.ok(spy.calledOnce);
- assert.equal(spy.getCall(0).args[0].maxLines, 3);
+ assert.ok(codeEditorEl.editor.getSession().getDocument().getLength(), 3);
});
-
- });
-
- describe('removeIncorrectAnnotations', function () {
-
- beforeEach(function () {
+ it('check default num lines #2', function () {
codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code}/>,
+ <ReactComponents.CodeEditor code={code2} setHeightToLineCount={true} />,
container
);
-
+ assert.ok(codeEditorEl.editor.getSession().getDocument().getLength(), 5);
});
-
- it('removes default errors that do not apply to CouchDB Views', function () {
- assert.equal(codeEditorEl.getAnnotations(), 0);
- });
-
- });
-
- describe('setEditorValue', function () {
-
- it('sets new code', function () {
+ it('check maxLines', function () {
codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code}/>,
+ <ReactComponents.CodeEditor code={code2} setHeightToLineCount={true} maxLines={2} />,
container
);
-
- codeEditorEl.setEditorValue(code2);
- assert.deepEqual(codeEditorEl.getValue(), code2);
+ assert.ok(codeEditorEl.editor.getSession().getDocument().getLength(), 2);
});
-
});
- describe('showEditorOnly', function () {
-
- it('only shows editor when showEditorOnly=true', function () {
+ describe('removeIncorrectAnnotations', function () {
+ beforeEach(function () {
codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code} showEditorOnly={true} />,
+ <ReactComponents.CodeEditor defaultCode={code} ignorableErrors={ignorableErrors} />,
container
);
- assert.notOk($(codeEditorEl.getDOMNode()).hasClass('control-group'));
});
-
- it('shows everything by default', function () {
- var codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code} />,
- container
- );
- assert.ok($(codeEditorEl.getDOMNode()).hasClass('control-group'));
+ it('removes default errors that do not apply to CouchDB Views', function () {
+ assert.equal(codeEditorEl.getAnnotations(), 0);
});
-
});
- describe('Zen Mode', function () {
- it('shows zen mode by default', function () {
- var container = document.createElement('div');
- var codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code} change={spy} docs="http://link.com" />,
- container
- );
- assert.equal($(codeEditorEl.getDOMNode()).find('.zen-editor-icon').length, 1);
- });
-
- it('omits zen mode if explicitly turned off', function () {
- var container = document.createElement('div');
- var codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code} change={spy} docs="http://link.com" allowZenMode={false} />,
- container
- );
- assert.equal($(codeEditorEl.getDOMNode()).find('.zen-editor-icon').length, 0);
- });
-
- it('updates parent editor after changing content in zen mode', function () {
- var container = document.createElement('div');
- var codeEditorEl = TestUtils.renderIntoDocument(
- <ReactComponents.CodeEditor code={code} change={spy} docs="http://link.com" />,
- container
- );
- });
- });
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/components/tests/zenModeSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/zenModeSpec.react.jsx b/app/addons/components/tests/zenModeSpec.react.jsx
index 5e51e1d..ec742d4 100644
--- a/app/addons/components/tests/zenModeSpec.react.jsx
+++ b/app/addons/components/tests/zenModeSpec.react.jsx
@@ -34,6 +34,7 @@ define([
afterEach(function () {
React.unmountComponentAtNode(container);
+ window.localStorage.removeItem('zenTheme');
});
describe('Toggle theme', function () {
@@ -44,11 +45,13 @@ define([
it('switch to light theme on click', function () {
TestUtils.Simulate.click($(el.getDOMNode()).find('.js-toggle-theme')[0]);
assert.ok($(el.getDOMNode()).hasClass('zen-theme-light'));
+ // reset
+ TestUtils.Simulate.click($(el.getDOMNode()).find('.js-toggle-theme')[0]);
});
});
describe('Closing zen mode', function () {
- it('defaults to dark theme', function () {
+ it('method called', function () {
TestUtils.Simulate.click($(el.getDOMNode()).find('.js-exit-zen-mode')[0]);
assert.ok(spy.calledOnce);
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/documents/index-editor/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/components.react.jsx b/app/addons/documents/index-editor/components.react.jsx
index 2450f62..719bf56 100644
--- a/app/addons/documents/index-editor/components.react.jsx
+++ b/app/addons/documents/index-editor/components.react.jsx
@@ -24,7 +24,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
var indexEditorStore = Stores.indexEditorStore;
var getDocUrl = app.helpers.getDocUrl;
var StyledSelect = ReactComponents.StyledSelect;
- var CodeEditor = ReactComponents.CodeEditor;
+ var CodeEditorPanel = ReactComponents.CodeEditorPanel;
var PaddedBorderedBox = ReactComponents.PaddedBorderedBox;
var ConfirmButton = ReactComponents.ConfirmButton;
var LoadLines = ReactComponents.LoadLines;
@@ -178,12 +178,13 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
customReduceSection;
if (this.state.hasCustomReduce) {
- customReduceSection = <CodeEditor
+ customReduceSection = <CodeEditorPanel
ref='reduceEditor'
id='reduce-function'
- code={this.state.reduce}
- change={this.updateReduceCode}
- docs={false} title={'Custom Reduce function'} />;
+ title={'Custom Reduce function'}
+ defaultCode={this.state.reduce}
+ blur={this.updateReduceCode}
+ />;
}
return (
@@ -319,16 +320,6 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
}, this);
},
- clearNotifications: function () {
- ['mapEditor', 'reduceEditor'].forEach(function (editorName) {
- if (editorName === 'reduceEditor' && !indexEditorStore.hasCustomReduce()) {
- return;
- }
- var editor = this.refs[editorName].getEditor();
- editor.editSaved();
- }.bind(this));
- },
-
saveView: function (event) {
event.preventDefault();
@@ -341,8 +332,6 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
return;
}
- this.clearNotifications();
-
Actions.saveView({
database: this.state.database,
newView: this.state.isNewView,
@@ -423,13 +412,13 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
</div>
<div className="control-group">
<PaddedBorderedBox>
- <CodeEditor
+ <CodeEditorPanel
id={'map-function'}
ref="mapEditor"
title={"Map function"}
- docs={getDocUrl('MAP_FUNCS')}
- change={this.updateMapCode}
- code={this.state.map} />
+ docLink={getDocUrl('MAP_FUNCS')}
+ blur={this.updateMapCode}
+ defaultCode={this.state.map} />
</PaddedBorderedBox>
</div>
<PaddedBorderedBox>
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx b/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
index 5fea728..4423572 100644
--- a/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
+++ b/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
@@ -221,7 +221,7 @@ define([
});
describe('Editor', function () {
- var container, editorEl, reduceStub;
+ var container, editorEl;
beforeEach(function () {
container = document.createElement('div');
@@ -235,13 +235,13 @@ define([
});
it('returns false on invalid map editor code', function () {
- var stub = sinon.stub(editorEl.refs.mapEditor, 'hadValidCode');
+ var stub = sinon.stub(editorEl.refs.mapEditor.getEditor(), 'hadValidCode');
stub.returns(false);
assert.notOk(editorEl.hasValidCode());
});
it('returns true on valid map editor code', function () {
- var stub = sinon.stub(editorEl.refs.mapEditor, 'hadValidCode');
+ var stub = sinon.stub(editorEl.refs.mapEditor.getEditor(), 'hadValidCode');
stub.returns(true);
assert.ok(editorEl.hasValidCode());
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f375c5/app/addons/documents/mango/mango.components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/mango/mango.components.react.jsx b/app/addons/documents/mango/mango.components.react.jsx
index 6dba412..303fbaa 100644
--- a/app/addons/documents/mango/mango.components.react.jsx
+++ b/app/addons/documents/mango/mango.components.react.jsx
@@ -30,7 +30,7 @@ function (app, FauxtonAPI, React, Stores, Actions,
var getDocUrl = app.helpers.getDocUrl;
var PaddedBorderedBox = ReactComponents.PaddedBorderedBox;
- var CodeEditor = ReactComponents.CodeEditor;
+ var CodeEditorPanel = ReactComponents.CodeEditorPanel;
var ConfirmButton = ReactComponents.ConfirmButton;
var MangoQueryEditorController = React.createClass({
@@ -45,7 +45,7 @@ function (app, FauxtonAPI, React, Stores, Actions,
changedQuery: mangoStore.getQueryFindCodeChanged(),
availableIndexes: mangoStore.getAvailableQueryIndexes(),
additionalIndexes: mangoStore.getAvailableAdditionalIndexes(),
- isLoading: mangoStore.getLoadingIndexes(),
+ isLoading: mangoStore.getLoadingIndexes()
};
},
@@ -144,13 +144,12 @@ function (app, FauxtonAPI, React, Stores, Actions,
</PaddedBorderedBox>
<form className="form-horizontal" onSubmit={this.props.onSubmit}>
<PaddedBorderedBox>
- <CodeEditor
+ <CodeEditorPanel
id="query-field"
ref="field"
title={this.props.title}
- docs={this.props.docs}
- code={this.props.exampleCode}
- disableUnload={true} />
+ docLink={this.props.docs}
+ defaultCode={this.props.exampleCode} />
{this.getChangedQueryText()}
</PaddedBorderedBox>
{this.getIndexBox()}