This is an automated email from the ASF dual-hosted git repository.

amaranhao pushed a commit to branch upgrade-to-eslint-v4
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git

commit c59243dc1215815b295049196215b08b2d5f5ec3
Author: Antonio Maranhao <[email protected]>
AuthorDate: Tue Jan 2 17:40:22 2018 -0500

    Enable eslint-react recommended rules
---
 .eslintrc                                          |  12 +-
 app/addons/activetasks/components.js               |   2 +-
 app/addons/auth/components/changepasswordform.js   |   4 +-
 app/addons/auth/components/createadminform.js      |   4 +-
 app/addons/auth/components/loginform.js            |  23 +-
 app/addons/components/components/bulkaction.js     |   4 +-
 app/addons/components/components/codeeditor.js     |   8 +-
 .../components/components/codeeditorpanel.js       |   7 +-
 .../components/components/deletedatabasemodal.js   |   2 +-
 app/addons/components/components/menudropdown.js   |   1 -
 .../components/components/stringeditmodal.js       |   6 +-
 app/addons/components/components/tray.js           |   1 -
 app/addons/components/components/zenmodeoverlay.js |   6 +-
 app/addons/config/components.js                    |   4 +-
 app/addons/databases/components.js                 |   4 +-
 app/addons/documents/__tests__/table-row.test.js   |   1 -
 app/addons/documents/changes/components.js         |  11 +-
 .../documents/components/header-docs-left.js       |   4 +-
 .../__tests__/doc-editor.components.test.js        |   2 +-
 app/addons/documents/doc-editor/components.js      |  12 +-
 .../index-editor/components/ReduceEditor.js        |   7 +-
 .../components/queryoptions/KeySearchFields.js     |  26 +-
 .../documents/mango/components/MangoIndexEditor.js |  10 +-
 .../documents/mango/components/MangoQueryEditor.js |  10 +-
 .../rev-browser/rev-browser.components.js          | 429 +++++++++++++++++++++
 app/addons/documents/sidebar/sidebar.js            |   4 +-
 app/addons/fauxton/notifications/notifications.js  |   2 +-
 app/addons/replication/controller.js               |   2 -
 28 files changed, 531 insertions(+), 77 deletions(-)

diff --git a/.eslintrc b/.eslintrc
index e062325..491dae8 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -6,7 +6,10 @@
     "react"
   ],
 
-  "extends": "eslint:recommended",
+  "extends": [
+    "eslint:recommended",
+    "plugin:react/recommended"
+  ],
 
   "rules": {
     "semi": [2, "always"],
@@ -19,15 +22,16 @@
     "computed-property-spacing": [2, "never"],
     "eol-last": 2,
     "no-case-declarations": 0,
-    "no-console": [2, { "allow": ["warn", "error", "info"] }],
+    "no-console": [1, { "allow": ["warn", "error", "info"] }],
     "no-mixed-spaces-and-tabs": [2, "smart-tabs"],
     "no-trailing-spaces": 2,
     "semi-spacing": [2, {"before": false, "after": true}],
     "space-in-parens": [2, "never"],
     "space-unary-ops": 2,
     "no-unused-vars": [2, {"varsIgnorePattern": "ReactDOM"}],
-    "react/jsx-uses-vars": 2,
-    "react/jsx-uses-react": 2
+    "react/display-name": 0,
+    // disabling for now since there are too many components without PropTypes
+    "react/prop-types": 0    
   },
 
   "parserOptions": {
diff --git a/app/addons/activetasks/components.js 
b/app/addons/activetasks/components.js
index c63927a..9ae1ce9 100644
--- a/app/addons/activetasks/components.js
+++ b/app/addons/activetasks/components.js
@@ -299,7 +299,7 @@ class ActiveTasksTableBody extends React.Component {
 
     return (
       <tr className="no-matching-database-on-search">
-        <td colSpan="6">No active {type} tasks match with filter: 
"{this.props.searchTerm}"</td>
+        <td colSpan="6">No active {type} tasks match with filter: 
&quot;{this.props.searchTerm}&quot;</td>
       </tr>
     );
   };
diff --git a/app/addons/auth/components/changepasswordform.js 
b/app/addons/auth/components/changepasswordform.js
index da77375..7ca0b8f 100644
--- a/app/addons/auth/components/changepasswordform.js
+++ b/app/addons/auth/components/changepasswordform.js
@@ -38,7 +38,7 @@ export class ChangePasswordForm extends React.Component {
   }
 
   componentDidMount() {
-    ReactDOM.findDOMNode(this.refs.password).focus();
+    this.passwordField.focus();
   }
 
   changePassword(e) {
@@ -59,7 +59,7 @@ export class ChangePasswordForm extends React.Component {
           <input
             id="password"
             type="password"
-            ref="password"
+            ref={node => this.passwordField = node}
             name="password"
             placeholder="Password"
             size="24"
diff --git a/app/addons/auth/components/createadminform.js 
b/app/addons/auth/components/createadminform.js
index 7229256..f92049b 100644
--- a/app/addons/auth/components/createadminform.js
+++ b/app/addons/auth/components/createadminform.js
@@ -37,7 +37,7 @@ export class CreateAdminForm extends React.Component {
   }
 
   componentDidMount() {
-    ReactDOM.findDOMNode(this.refs.username).focus();
+    this.usernameField.focus();
   }
 
   createAdmin(e) {
@@ -74,7 +74,7 @@ export class CreateAdminForm extends React.Component {
           <input
             id="username"
             type="text"
-            ref="username"
+            ref={node => this.usernameField = node}
             name="name"
             placeholder="Username"
             size="24"
diff --git a/app/addons/auth/components/loginform.js 
b/app/addons/auth/components/loginform.js
index 528d173..070a337 100644
--- a/app/addons/auth/components/loginform.js
+++ b/app/addons/auth/components/loginform.js
@@ -24,10 +24,13 @@ class LoginForm extends React.Component {
       password: ""
     };
   }
-  onInputChange(e) {
-    this.state[e.target.name] = e.target.value;
-    this.setState(this.state);
+  onUsernameChange(e) {
+    this.setState({username: e.target.value});
   }
+  onPasswordChange(e) {
+    this.setState({password: e.target.value});
+  }
+
   submit(e) {
     e.preventDefault();
     if (!this.checkUnrecognizedAutoFill()) {
@@ -42,10 +45,10 @@ class LoginForm extends React.Component {
     }
     let username = this.props.testBlankUsername
       ? this.props.testBlankUsername
-      : ReactDOM.findDOMNode(this.refs.username).value;
+      : this.usernameField.value;
     let password = this.props.testBlankPassword
       ? this.props.testBlankPassword
-      : ReactDOM.findDOMNode(this.refs.password).value;
+      : this.passwordField.value;
     this.setState({ username: username, password: password }); // doesn't set 
immediately, hence separate login() call
     this.login(username, password);
 
@@ -55,7 +58,7 @@ class LoginForm extends React.Component {
     login(username, password, this.props.urlBack);
   }
   componentDidMount() {
-    ReactDOM.findDOMNode(this.refs.username).focus();
+    this.usernameField.focus();
   }
   render() {
     return (
@@ -70,10 +73,10 @@ class LoginForm extends React.Component {
                 id="username"
                 type="text"
                 name="username"
-                ref="username"
+                ref={node => this.usernameField = node}
                 placeholder="Username"
                 size="24"
-                onChange={this.onInputChange.bind(this)}
+                onChange={this.onUsernameChange.bind(this)}
                 value={this.state.username}
               />
               <br />
@@ -81,10 +84,10 @@ class LoginForm extends React.Component {
                 id="password"
                 type="password"
                 name="password"
-                ref="password"
+                ref={node => this.passwordField = node}
                 placeholder="Password"
                 size="24"
-                onChange={this.onInputChange.bind(this)}
+                onChange={this.onPasswordChange.bind(this)}
                 value={this.state.password}
               />
               <br />
diff --git a/app/addons/components/components/bulkaction.js 
b/app/addons/components/components/bulkaction.js
index 12958be..fc63a81 100644
--- a/app/addons/components/components/bulkaction.js
+++ b/app/addons/components/components/bulkaction.js
@@ -70,14 +70,14 @@ export class BulkActionComponent extends React.Component {
   };
 
   selectAll = () => {
-    this.refs.bulkActionPopover.hide();
+    this.bulkActionPopover.hide();
     this.props.selectAll();
   };
 
   getOverlay = () => {
     return (
       <OverlayTrigger
-        ref="bulkActionPopover"
+        ref={node => this.bulkActionPopover = node}
         trigger="click"
         placement="bottom"
         rootClose={true}
diff --git a/app/addons/components/components/codeeditor.js 
b/app/addons/components/components/codeeditor.js
index 602de08..b9f8bf8 100644
--- a/app/addons/components/components/codeeditor.js
+++ b/app/addons/components/components/codeeditor.js
@@ -78,7 +78,7 @@ export class CodeEditor extends React.Component {
   };
 
   setupAce = (props, shouldUpdateCode) => {
-    this.editor = ace.edit(ReactDOM.findDOMNode(this.refs.ace));
+    this.editor = ace.edit(this.ace);
 
     // suppresses an Ace editor error
     this.editor.$blockScrolling = Infinity;
@@ -351,13 +351,13 @@ export class CodeEditor extends React.Component {
   render() {
     return (
       <div>
-        <div ref="ace" className="js-editor" id={this.props.id}></div>
-        <button ref="stringEditIcon" className="btn string-edit" title="Edit 
string" disabled={!this.state.stringEditIconVisible}
+        <div ref={node => this.ace = node} className="js-editor" 
id={this.props.id}></div>
+        <button ref={node => this.stringEditIcon = node} className="btn 
string-edit" title="Edit string" disabled={!this.state.stringEditIconVisible}
           style={this.state.stringEditIconStyle} 
onClick={this.openStringEditModal}>
           <i className="icon icon-edit"></i>
         </button>
         <StringEditModal
-          ref="stringEditModal"
+          ref={node => this.stringEditModal = node}
           visible={this.state.stringEditModalVisible}
           value={this.state.stringEditModalValue}
           onSave={this.saveStringEditModal}
diff --git a/app/addons/components/components/codeeditorpanel.js 
b/app/addons/components/components/codeeditorpanel.js
index 714304d..4853042 100644
--- a/app/addons/components/components/codeeditorpanel.js
+++ b/app/addons/components/components/codeeditorpanel.js
@@ -63,6 +63,7 @@ export class CodeEditorPanel extends React.Component {
           data-bypass="true"
           href={this.props.docLink}
           target="_blank"
+          rel="noopener noreferrer"
         >
           <i className="icon-question-sign"></i>
         </a>
@@ -85,7 +86,7 @@ export class CodeEditorPanel extends React.Component {
   enterZenMode = () => {
     this.setState({
       zenModeEnabled: true,
-      code: this.refs.codeEditor.getValue()
+      code: this.codeEditor.getValue()
     });
   };
 
@@ -95,7 +96,7 @@ export class CodeEditorPanel extends React.Component {
   };
 
   getEditor = () => {
-    return this.refs.codeEditor;
+    return this.codeEditor;
   };
 
   getValue = () => {
@@ -127,7 +128,7 @@ export class CodeEditorPanel extends React.Component {
         </label>
         <CodeEditor
           id={this.props.id}
-          ref="codeEditor"
+          ref={node => this.codeEditor = node}
           mode="javascript"
           defaultCode={this.state.code}
           showGutter={true}
diff --git a/app/addons/components/components/deletedatabasemodal.js 
b/app/addons/components/components/deletedatabasemodal.js
index e8213fe..65a2f33 100644
--- a/app/addons/components/components/deletedatabasemodal.js
+++ b/app/addons/components/components/deletedatabasemodal.js
@@ -94,7 +94,7 @@ export class DeleteDatabaseModal extends React.Component {
           <p>
             Warning: This action will permanently delete <code>{dbId}</code>.
             To confirm the deletion of the database and all of the
-            database's documents, you must enter the database's name.
+            database&apos;s documents, you must enter the database&apos;s name.
           </p>
           <input
             type="text"
diff --git a/app/addons/components/components/menudropdown.js 
b/app/addons/components/components/menudropdown.js
index a2e90c4..d10f014 100644
--- a/app/addons/components/components/menudropdown.js
+++ b/app/addons/components/components/menudropdown.js
@@ -72,7 +72,6 @@ export class MenuDropDown extends React.Component {
         <CustomMenu bsRole="menu" className="arrow">
           {menuItems}
         </CustomMenu>
-        }
       </Dropdown>
     );
   }
diff --git a/app/addons/components/components/stringeditmodal.js 
b/app/addons/components/components/stringeditmodal.js
index 9b8f06f..f960dd7 100644
--- a/app/addons/components/components/stringeditmodal.js
+++ b/app/addons/components/components/stringeditmodal.js
@@ -56,7 +56,7 @@ export class StringEditModal extends React.Component {
   }
 
   initEditor = (val) => {
-    this.editor = ace.edit(ReactDOM.findDOMNode(this.refs.stringEditor));
+    this.editor = ace.edit(this.stringEditor);
     this.editor.$blockScrolling = Infinity; // suppresses an Ace editor error
     this.editor.setShowPrintMargin(false);
     this.editor.setOption('highlightActiveLine', true);
@@ -80,7 +80,9 @@ export class StringEditModal extends React.Component {
         </Modal.Header>
         <Modal.Body>
           <div id="modal-error" className="hide alert alert-error"/>
-          <div id="string-editor-wrapper"><div ref="stringEditor" 
className="doc-code"></div></div>
+          <div id="string-editor-wrapper">
+            <div ref={node => this.stringEditor = node} 
className="doc-code"></div>
+          </div>
         </Modal.Body>
         <Modal.Footer>
           <a className="cancel-link" onClick={this.closeModal}>Cancel</a>
diff --git a/app/addons/components/components/tray.js 
b/app/addons/components/components/tray.js
index 41be571..335c36a 100644
--- a/app/addons/components/components/tray.js
+++ b/app/addons/components/components/tray.js
@@ -82,7 +82,6 @@ export class TrayContents extends React.Component {
        placement={"bottom"}
        container={this.props.container}
        rootClose={true}
-       target={() => ReactDOM.findDOMNode(this.refs.target)}
        onEnter={this.props.onEnter}
       >
         <TransitionMotion
diff --git a/app/addons/components/components/zenmodeoverlay.js 
b/app/addons/components/components/zenmodeoverlay.js
index 7cffb62..2834123 100644
--- a/app/addons/components/components/zenmodeoverlay.js
+++ b/app/addons/components/components/zenmodeoverlay.js
@@ -56,7 +56,7 @@ export class ZenModeOverlay extends React.Component {
   };
 
   getValue = () => {
-    return this.refs.ace.getValue();
+    return this.ace.getValue();
   };
 
   toggleTheme = () => {
@@ -107,7 +107,7 @@ export class ZenModeOverlay extends React.Component {
             </li>
             <li>
               <OverlayTrigger placement="left" overlay={tooltipTheme}>
-                <span ref="theme"
+                <span
                   className="fonticon fonticon-picture js-toggle-theme"
                   data-container=".zen-mode-controls .tooltips"
                   title="Switch zen theme"
@@ -119,7 +119,7 @@ export class ZenModeOverlay extends React.Component {
           <div className="tooltips"></div>
         </div>
         <CodeEditor
-          ref="ace"
+          ref={node => this.ace = node}
           autoFocus={true}
           theme={themes[this.state.theme]}
           defaultCode={this.props.defaultCode}
diff --git a/app/addons/config/components.js b/app/addons/config/components.js
index 3bd0d68..6fd6240 100644
--- a/app/addons/config/components.js
+++ b/app/addons/config/components.js
@@ -366,7 +366,7 @@ class AddOptionButton extends React.Component {
         <Button
           id="add-option-button"
           onClick={this.togglePopover.bind(this)}
-          ref="target">
+          ref={node => this.target = node}>
           <i className="icon icon-plus header-icon"></i>
           Add Option
         </Button>
@@ -376,7 +376,7 @@ class AddOptionButton extends React.Component {
           onHide={this.hidePopover.bind(this)}
           placement="bottom"
           rootClose={true}
-          target={() => ReactDOM.findDOMNode(this.refs.target)}>
+          target={() => this.target}>
           {this.getPopover()}
         </Overlay>
       </div>
diff --git a/app/addons/databases/components.js 
b/app/addons/databases/components.js
index 0bb9c31..f339679 100644
--- a/app/addons/databases/components.js
+++ b/app/addons/databases/components.js
@@ -227,7 +227,7 @@ class AddDatabaseWidget extends React.Component {
   };
 
   focusInput = () => {
-    ReactDOM.findDOMNode(this.refs.newDbName).focus();
+    this.newDbName.focus();
   };
 
   onKeyUpInInput = (e) => {
@@ -258,7 +258,7 @@ class AddDatabaseWidget extends React.Component {
           <span className="add-on">Create Database</span>
           <input
             id="js-new-database-name"
-            ref="newDbName"
+            ref={node => this.newDbName = node}
             type="text"
             value={this.state.databaseName}
             onChange={this.onChange} onKeyUp={this.onKeyUpInInput}
diff --git a/app/addons/documents/__tests__/table-row.test.js 
b/app/addons/documents/__tests__/table-row.test.js
index b2636c8..5f727f3 100644
--- a/app/addons/documents/__tests__/table-row.test.js
+++ b/app/addons/documents/__tests__/table-row.test.js
@@ -41,7 +41,6 @@ describe('Docs Table Row', () => {
     const wrapper = shallow(<TableRow
       onClick={sinon.stub()}
       docChecked={sinon.stub()}
-      isSelected={sinon.stub()}
       el={elem}
       data={data}
       index={0}
diff --git a/app/addons/documents/changes/components.js 
b/app/addons/documents/changes/components.js
index 60afcd8..cb5ff21 100644
--- a/app/addons/documents/changes/components.js
+++ b/app/addons/documents/changes/components.js
@@ -178,7 +178,7 @@ class AddFilterForm extends React.Component {
   }
 
   focusFilterField () {
-    ReactDOM.findDOMNode(this.refs.addItem).focus();
+    this.addItem.focus();
   }
 
   inputClassNames () {
@@ -194,8 +194,13 @@ class AddFilterForm extends React.Component {
       <form className="form-inline js-filter-form" onSubmit={this.submitForm}>
         <fieldset>
           <i className="fonticon-filter" />
-          <input type="text" ref="addItem" className={this.inputClassNames()} 
placeholder="Sequence or ID"
-                 onChange={(e) => this.setState({ filter: e.target.value })} 
value={this.state.filter} />
+          <input
+            type="text"
+            ref={node => this.addItem = node}
+            className={this.inputClassNames()}
+            placeholder="Sequence or ID"
+            onChange={(e) => this.setState({ filter: e.target.value })}
+            value={this.state.filter} />
           <button type="submit" className="btn btn-secondary">Filter</button>
           <div className="help-block"></div>
         </fieldset>
diff --git a/app/addons/documents/components/header-docs-left.js 
b/app/addons/documents/components/header-docs-left.js
index 1d14778..8be0e58 100644
--- a/app/addons/documents/components/header-docs-left.js
+++ b/app/addons/documents/components/header-docs-left.js
@@ -31,11 +31,11 @@ function getDropdownItems (items) {
 
     if (el.title && el.links) {
       const items = el.links.map((subel) => {
-        return <Item onClick={subel.onClick} icon={subel.icon} 
url={subel.url}>{subel.title}</Item>;
+        return <Item key={subel.title} onClick={subel.onClick} 
icon={subel.icon} url={subel.url}>{subel.title}</Item>;
       });
 
       return ([
-        <li className="header-label">{el.title}</li>,
+        <li key={el.title} className="header-label">{el.title}</li>,
         items
       ]);
     }
diff --git 
a/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js 
b/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
index bc72537..178eb47 100644
--- a/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
+++ b/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
@@ -202,7 +202,7 @@ describe("Custom Extension Buttons", () => {
       render() {
         return (
           <div>
-            <button>Oh no she di'n't!</button>
+            <button>Oh no she di&apos;n&apos;t!</button>
             <span id="testDatabaseName">{this.props.database.id}</span>
           </div>
         );
diff --git a/app/addons/documents/doc-editor/components.js 
b/app/addons/documents/doc-editor/components.js
index 7c9afbc..8f98824 100644
--- a/app/addons/documents/doc-editor/components.js
+++ b/app/addons/documents/doc-editor/components.js
@@ -60,7 +60,7 @@ class DocEditorController extends React.Component {
     return (
       <GeneralComponents.CodeEditor
         id="doc-editor"
-        ref="docEditor"
+        ref={node => this.docEditor = node}
         defaultCode={code}
         mode="json"
         autoFocus={true}
@@ -110,7 +110,7 @@ class DocEditorController extends React.Component {
   };
 
   getEditor = () => {
-    return (this.refs.docEditor) ? this.refs.docEditor.getEditor() : null;
+    return (this.docEditor) ? this.docEditor.getEditor() : null;
   };
 
   checkDocIsValid = () => {
@@ -124,7 +124,7 @@ class DocEditorController extends React.Component {
   };
 
   clearChanges = () => {
-    this.refs.docEditor.clearChanges();
+    this.docEditor.clearChanges();
   };
 
   getExtensionIcons = () => {
@@ -184,7 +184,7 @@ class DocEditorController extends React.Component {
         </div>
 
         <UploadModal
-          ref="uploadModal"
+          ref={node => this.uploadModal = node}
           visible={this.state.uploadModalVisible}
           doc={this.state.doc} />
         <CloneDocModal
@@ -346,7 +346,7 @@ class UploadModal extends React.Component {
         <Modal.Body>
           <div className={errorClasses}>{this.state.errorMessage}</div>
           <div>
-            <form ref="uploadForm" className="form">
+            <form ref={node => this.uploadForm = node} className="form">
               <p>
                 Select a file to upload as an attachment to this document. 
Uploading a file saves the document as a new
                 revision.
@@ -430,7 +430,7 @@ class CloneDocModal extends React.Component {
             <p>
               You can modify the following generated ID for your new document.
             </p>
-            <input ref="newDocId" type="text" autoFocus={true} 
className="input-block-level"
+            <input ref={node => this.newDocId = node} type="text" 
autoFocus={true} className="input-block-level"
               onChange={this.docIDChange} value={this.state.uuid} />
           </form>
         </Modal.Body>
diff --git a/app/addons/documents/index-editor/components/ReduceEditor.js 
b/app/addons/documents/index-editor/components/ReduceEditor.js
index 3158f1c..3b4a4c1 100644
--- a/app/addons/documents/index-editor/components/ReduceEditor.js
+++ b/app/addons/documents/index-editor/components/ReduceEditor.js
@@ -65,11 +65,11 @@ export default class ReduceEditor extends Component {
       return this.state.reduce;
     }
 
-    return this.refs.reduceEditor.getValue();
+    return this.reduceEditor.getValue();
   }
 
   getEditor() {
-    return this.refs.reduceEditor.getEditor();
+    return this.reduceEditor.getEditor();
   }
 
   updateReduceCode(code) {
@@ -86,7 +86,7 @@ export default class ReduceEditor extends Component {
 
     if (this.state.hasCustomReduce) {
       customReduceSection = <CodeEditorPanel
-        ref='reduceEditor'
+        ref={node => this.reduceEditor = node}
         id='reduce-function'
         title={'Custom Reduce function'}
         defaultCode={this.state.reduce}
@@ -115,7 +115,6 @@ export default class ReduceEditor extends Component {
             selectId="reduce-function-selector"
             selectValue={this.state.reduceSelectedOption} />
         </div>
-
         {customReduceSection}
       </div>
     );
diff --git 
a/app/addons/documents/index-results/components/queryoptions/KeySearchFields.js 
b/app/addons/documents/index-results/components/queryoptions/KeySearchFields.js
index 76d192a..1fb2f90 100644
--- 
a/app/addons/documents/index-results/components/queryoptions/KeySearchFields.js
+++ 
b/app/addons/documents/index-results/components/queryoptions/KeySearchFields.js
@@ -28,8 +28,8 @@ export default class KeySearchFields extends React.Component {
 
   updateBetweenKeys () {
     this.props.updateBetweenKeys({
-      startkey: ReactDOM.findDOMNode(this.refs.startkey).value,
-      endkey: ReactDOM.findDOMNode(this.refs.endkey).value,
+      startkey: this.startkey.value,
+      endkey: this.endkey.value,
       include: this.props.betweenKeys.include
     });
   }
@@ -91,13 +91,29 @@ export default class KeySearchFields extends 
React.Component {
             <div className="controls controls-row">
               <div>
                 <label htmlFor="startkey" className="drop-down">Start 
key</label>
-                <input id="startkey" ref="startkey" type="text" 
onChange={this.updateBetweenKeys.bind(this)} 
value={this.props.betweenKeys.startkey} placeholder='e.g., "1234"' />
+                <input
+                  id="startkey"
+                  ref={node => this.startkey = node}
+                  type="text"
+                  onChange={this.updateBetweenKeys.bind(this)}
+                  value={this.props.betweenKeys.startkey}
+                  placeholder='e.g., "1234"' />
               </div>
               <div>
                 <label htmlFor="endkey" className="drop-down">End key</label>
-                <input id="endkey" ref="endkey" 
onChange={this.updateBetweenKeys.bind(this)} 
value={this.props.betweenKeys.endkey} type="text" placeholder='e.g., "1234"'/>
+                <input
+                  id="endkey"
+                  ref={node => this.endkey = node}
+                  onChange={this.updateBetweenKeys.bind(this)}
+                  value={this.props.betweenKeys.endkey}
+                  type="text"
+                  placeholder='e.g., "1234"'/>
                 <div className="controls include-end-key-row checkbox 
controls-row inline">
-                  <input id="qoIncludeEndKeyInResults" ref="inclusive_end" 
type="checkbox" onChange={this.updateInclusiveEnd.bind(this)} 
checked={this.props.betweenKeys.include}/>
+                  <input
+                    id="qoIncludeEndKeyInResults"
+                    type="checkbox"
+                    onChange={this.updateInclusiveEnd.bind(this)}
+                    checked={this.props.betweenKeys.include}/>
                   <label htmlFor="qoIncludeEndKeyInResults">Include End Key in 
results</label>
                 </div>
               </div>
diff --git a/app/addons/documents/mango/components/MangoIndexEditor.js 
b/app/addons/documents/mango/components/MangoIndexEditor.js
index 6e98af7..739a651 100644
--- a/app/addons/documents/mango/components/MangoIndexEditor.js
+++ b/app/addons/documents/mango/components/MangoIndexEditor.js
@@ -48,15 +48,15 @@ export default class MangoIndexEditor extends Component {
   }
 
   setEditorValue(newValue = '') {
-    return this.refs.codeEditor.getEditor().setValue(newValue);
+    return this.codeEditor.getEditor().setValue(newValue);
   }
 
   getEditorValue() {
-    return this.refs.codeEditor.getValue();
+    return this.codeEditor.getValue();
   }
 
   editorHasErrors() {
-    return this.refs.codeEditor.getEditor().hasErrors();
+    return this.codeEditor.getEditor().hasErrors();
   }
 
   onTemplateSelected(selectedItem) {
@@ -72,7 +72,7 @@ export default class MangoIndexEditor extends Component {
             <ReactSelect
               className="mango-select"
               options={this.props.templates}
-              ref="templates"
+              ref={node => this.templates = node}
               placeholder="Examples"
               searchable={false}
               clearable={false}
@@ -83,7 +83,7 @@ export default class MangoIndexEditor extends Component {
           <PaddedBorderedBox>
             <CodeEditorPanel
               id="query-field"
-              ref="codeEditor"
+              ref={node => this.codeEditor = node}
               title="Index"
               docLink={getDocUrl('MANGO_INDEX')}
               defaultCode={this.props.queryIndexCode} />
diff --git a/app/addons/documents/mango/components/MangoQueryEditor.js 
b/app/addons/documents/mango/components/MangoQueryEditor.js
index 88e9a2f..f04a158 100644
--- a/app/addons/documents/mango/components/MangoQueryEditor.js
+++ b/app/addons/documents/mango/components/MangoQueryEditor.js
@@ -46,15 +46,15 @@ export default class MangoQueryEditor extends Component {
   }
 
   setEditorValue (newValue = '') {
-    return this.refs.codeEditor.getEditor().setValue(newValue);
+    return this.codeEditor.getEditor().setValue(newValue);
   }
 
   getEditorValue () {
-    return this.refs.codeEditor.getValue();
+    return this.codeEditor.getValue();
   }
 
   editorHasErrors () {
-    return this.refs.codeEditor.getEditor().hasErrors();
+    return this.codeEditor.getEditor().hasErrors();
   }
 
   onHistorySelected(selectedItem) {
@@ -69,7 +69,7 @@ export default class MangoQueryEditor extends Component {
             <ReactSelect
               className="mango-select"
               options={this.props.history}
-              ref="history"
+              ref={node => this.history = node}
               placeholder="Query history"
               searchable={false}
               clearable={false}
@@ -80,7 +80,7 @@ export default class MangoQueryEditor extends Component {
           <PaddedBorderedBox>
             <CodeEditorPanel
               id="query-field"
-              ref="codeEditor"
+              ref={node => this.codeEditor = node}
               title={this.props.editorTitle}
               docLink={getDocUrl('MANGO_SEARCH')}
               defaultCode={this.props.queryFindCode} />
diff --git a/app/addons/documents/rev-browser/rev-browser.components.js 
b/app/addons/documents/rev-browser/rev-browser.components.js
new file mode 100644
index 0000000..a6e4504
--- /dev/null
+++ b/app/addons/documents/rev-browser/rev-browser.components.js
@@ -0,0 +1,429 @@
+import app from "../../../app";
+
+// 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.
+
+import PropTypes from 'prop-types';
+
+import React from "react";
+import ReactDOM from "react-dom";
+import RevActions from "./rev-browser.actions";
+import RevStores from "./rev-browser.stores";
+import ReactComponents from "../../components/react-components";
+import { ButtonGroup, Button, Modal } from "react-bootstrap";
+import ReactSelect from "react-select";
+import jdp from "jsondiffpatch";
+import jdpformatters from "jsondiffpatch/src/formatters/html";
+import ace from "brace";
+import "jsondiffpatch/public/formatters-styles/html.css";
+
+const storageKeyDeleteConflictsModal = 'deleteConflictsHideModal';
+
+const store = RevStores.revBrowserStore;
+const ConfirmButton = ReactComponents.ConfirmButton;
+
+require('brace/ext/static_highlight');
+const highlight = ace.acequire('ace/ext/static_highlight');
+
+require('brace/mode/json');
+const JavaScriptMode = ace.acequire('ace/mode/json').Mode;
+
+require('brace/theme/idle_fingers');
+const theme = ace.acequire('ace/theme/idle_fingers');
+
+
+class DiffyController extends React.Component {
+
+  constructor (props) {
+    super(props);
+
+    this.state = this.getStoreState();
+  }
+
+  getStoreState () {
+
+    return {
+      tree: store.getRevTree(),
+      ours: store.getOurs(),
+      theirs: store.getTheirs(),
+      conflictingRevs: store.getConflictingRevs(),
+      dropdownData: store.getDropdownData(),
+      isDiffViewEnabled: store.getIsDiffViewEnabled(),
+      databaseName: store.getDatabaseName()
+    };
+  }
+
+  componentDidMount () {
+    store.on('change', this.onChange, this);
+  }
+
+  componentWillUnmount () {
+    store.off('change', this.onChange);
+  }
+
+  onChange () {
+    this.setState(this.getStoreState());
+  }
+
+  toggleDiffView (enableDiff) {
+    RevActions.toggleDiffView(enableDiff);
+  }
+
+  render () {
+    const {tree, ours, theirs, conflictingRevs, isDiffViewEnabled} = 
this.state;
+
+    if (!tree) {
+      return null;
+    }
+
+    // no conflicts happened for this doc
+    if (!theirs || !conflictingRevs.length) {
+      return <div style={{textAlign: 'center', color: '#fff'}}><h2>No 
conflicts</h2></div>;
+    }
+
+    return (
+      <div className="revision-wrapper scrollable">
+        <RevisionBrowserControls {...this.state} />
+        <div className="revision-view-controls">
+          <ButtonGroup className="two-sides-toggle-button">
+            <Button
+              style={{width: '120px'}}
+              className={isDiffViewEnabled ? 'active' : ''}
+              onClick={this.toggleDiffView.bind(this, true)}
+            >
+              <i className="icon-columns" /> Diff
+            </Button>
+            <Button
+              style={{width: '120px'}}
+              className={isDiffViewEnabled ? '' : 'active'}
+              onClick={this.toggleDiffView.bind(this, false)}
+            >
+              <i className="icon-file-text" /> Document
+            </Button>
+          </ButtonGroup>
+        </div>
+
+        {isDiffViewEnabled ?
+          <RevisionDiffArea ours={ours} theirs={theirs} /> :
+          <SplitScreenArea ours={ours} theirs={theirs} /> }
+      </div>
+    );
+  }
+}
+
+
+class SplitScreenArea extends React.Component {
+
+  constructor (props) {
+    super(props);
+  }
+
+  componentDidUpdate () {
+    this.hightlightAfterRender();
+  }
+
+  componentDidMount () {
+    this.hightlightAfterRender();
+  }
+
+  hightlightAfterRender () {
+    const format = (input) => { return JSON.stringify(input, null, '  '); };
+
+    const jsmode = new JavaScriptMode();
+    const left = this.revLeftOurs;
+    const right = this.revRightTheirs;
+
+    const leftRes = highlight.render(format(this.props.ours), jsmode, theme, 
0, true);
+    left.innerHTML = leftRes.html;
+    const rightRes = highlight.render(format(this.props.theirs), jsmode, 
theme, 0, true);
+    right.innerHTML = rightRes.html;
+  }
+
+  render () {
+    const {ours, theirs} = this.props;
+
+    if (!ours || !theirs) {
+      return <div></div>;
+    }
+
+    return (
+      <div className="revision-split-area">
+        <div data-id="ours" style={{width: '50%'}}>
+          <div style={{marginBottom: '20px'}}>{ours._rev} (Server-Selected 
Rev)</div>
+          <pre ref={node => this.revLeftOurs = node}></pre>
+        </div>
+
+        <div data-id="theirs" style={{width: '50%'}}>
+          <div style={{marginBottom: '20px'}}>{theirs._rev}</div>
+          <pre ref={node => this.revRightTheirs = node}></pre>
+        </div>
+      </div>
+    );
+  }
+}
+
+const RevisionDiffArea = ({ours, theirs}) => {
+  if (!ours || !theirs) {
+    return <div></div>;
+  }
+
+  const delta = jdp.diff(ours, theirs);
+  const html = jdpformatters.format(delta, ours);
+
+  return (
+    <div className="revision-diff-area">
+      <div
+        style={{marginTop: '30px'}}
+        dangerouslySetInnerHTML={{__html: html}}
+      ></div>
+    </div>
+  );
+};
+RevisionDiffArea.propTypes = {
+  ours: PropTypes.object,
+  theirs: PropTypes.object,
+  currentRev: PropTypes.string
+};
+
+
+const ConflictingRevisionsDropDown = ({options, selected, onRevisionClick, 
onBackwardClick, onForwardClick}) => {
+  return (
+    <div className="conflicting-revs-dropdown">
+      <BackForwardControls backward onClick={onBackwardClick} />
+      <div style={{width: '345px', margin: '0 5px'}}>
+        <ReactSelect
+          name="form-field-name"
+          value={selected}
+          options={options}
+          clearable={false}
+          onChange={onRevisionClick} />
+      </div>
+      <BackForwardControls forward onClick={onForwardClick} />
+    </div>
+  );
+};
+ConflictingRevisionsDropDown.propTypes = {
+  options: PropTypes.array.isRequired,
+  selected: PropTypes.string.isRequired,
+  onRevisionClick: PropTypes.func.isRequired,
+  onBackwardClick: PropTypes.func.isRequired,
+  onForwardClick: PropTypes.func.isRequired,
+};
+
+class RevisionBrowserControls extends React.Component {
+
+  constructor (props) {
+    super(props);
+
+    this.state = {showModal: false};
+  }
+
+  onRevisionClick (revTheirs) {
+
+    RevActions.chooseLeaves(this.props.ours, revTheirs.value);
+  }
+
+  onForwardClick () {
+    const conflictingRevs = this.props.conflictingRevs;
+    const index = conflictingRevs.indexOf(this.props.theirs._rev);
+
+    const next = conflictingRevs[index + 1];
+
+    if (!next) {
+      return;
+    }
+
+    RevActions.chooseLeaves(this.props.ours, next);
+  }
+
+  onBackwardClick () {
+    const conflictingRevs = this.props.conflictingRevs;
+    const index = conflictingRevs.indexOf(this.props.theirs._rev);
+
+    const next = conflictingRevs[index - 1];
+
+    if (!next) {
+      return;
+    }
+
+    RevActions.chooseLeaves(this.props.ours, next);
+  }
+
+  selectAsWinner (docToWin, doNotShowModalAgain) {
+    if (doNotShowModalAgain) {
+      app.utils.localStorageSet(storageKeyDeleteConflictsModal, true);
+    }
+
+    RevActions.selectRevAsWinner(this.props.databaseName, docToWin._id, 
this.props.tree.paths, docToWin._rev);
+  }
+
+  onSelectAsWinnerClick (docToWin) {
+    if (app.utils.localStorageGet(storageKeyDeleteConflictsModal) !== true) {
+      RevActions.showConfirmModal(true, docToWin);
+      return;
+    }
+
+    this.selectAsWinner(docToWin);
+  }
+
+  render () {
+    const {tree} = this.props;
+    const cellStyle = {paddingRight: '30px'};
+
+    return (
+      <div className="revision-browser-controls">
+        <ConfirmModal onConfirm={this.selectAsWinner.bind(this)} />
+        <table style={{margin: '10px 60px', width: '100%'}}>
+          <tbody>
+            <tr style={{height: '60px'}}>
+              <td style={cellStyle}>Server-Selected Rev: </td>
+              <td style={cellStyle}>
+                <div style={{lineHeight: '36px', height: '36px', width: 
'337px', color: '#000', backgroundColor: '#ffbbbb'}}>
+                  <b style={{paddingLeft: '10px'}}>{tree.winner}</b>
+                </div>
+              </td>
+              <td>
+                <ConfirmButton
+                  onClick={this.onSelectAsWinnerClick.bind(this, 
this.props.ours)}
+                  style={{marginRight: '10px', width: '220px'}}
+                  text="Delete Other Conflicts"
+                  buttonType="btn-secondary"
+                  customIcon="icon-trophy" />
+              </td>
+            </tr>
+            <tr style={{height: '60px'}}>
+              <td style={cellStyle}>Conflicting Revisions: </td>
+              <td style={cellStyle}>
+                <ConflictingRevisionsDropDown
+                  onRevisionClick={this.onRevisionClick.bind(this)}
+                  onForwardClick={this.onForwardClick.bind(this)}
+                  onBackwardClick={this.onBackwardClick.bind(this)}
+                  options={this.props.dropdownData}
+                  selected={this.props.theirs._rev} />
+              </td>
+              <td>
+                <ConfirmButton
+                  data-id="button-select-theirs"
+                  onClick={this.onSelectAsWinnerClick.bind(this, 
this.props.theirs)}
+                  style={{marginRight: '10px', width: '220px'}}
+                  text="Select as Winner"
+                  buttonType="btn-secondary"
+                  customIcon="icon-trophy" />
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+
+    );
+  }
+}
+RevisionBrowserControls.propTypes = {
+  tree: PropTypes.object.isRequired,
+  ours: PropTypes.object.isRequired,
+  conflictingRevs: PropTypes.array.isRequired,
+};
+
+class ConfirmModal extends React.Component {
+
+  constructor (props) {
+    super(props);
+
+    this.state = this.getStoreState();
+  }
+
+  getStoreState () {
+    return {
+      show: store.getShowConfirmModal(),
+      docToWin: store.getDocToWin(),
+      checked: false
+    };
+  }
+
+  componentDidMount () {
+    store.on('change', this.onChange, this);
+  }
+
+  componentWillUnmount () {
+    store.off('change', this.onChange);
+  }
+
+  onChange () {
+    this.setState(this.getStoreState());
+  }
+
+  close () {
+    RevActions.showConfirmModal(false, null);
+  }
+
+  onDeleteConflicts () {
+    const hideModal = this.state.checked;
+    this.props.onConfirm(this.state.docToWin, hideModal);
+  }
+
+  render () {
+    return (
+      <Modal dialogClassName="delete-conflicts-modal" show={this.state.show} 
onHide={this.close}>
+        <Modal.Header closeButton={false}>
+          <Modal.Title>Solve Conflicts</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          <p>
+          <i className="icon-warning-sign"></i> Do you want to delete all 
conflicting revisions for this document?
+          </p>
+
+
+        </Modal.Body>
+        <Modal.Footer>
+          <div style={{float: 'left', marginTop: '10px'}}>
+            <label>
+              <input
+                style={{margin: '0 5px 3px 0'}}
+                onChange={() => { this.setState({checked: !this.state.checked 
}); }}
+                type="checkbox" />
+                Do not show this warning message again
+            </label>
+          </div>
+          <a
+            style={{marginRight: '10px', cursor: 'pointer'}}
+            onClick={this.close}
+            data-bypass="true"
+          >
+            Cancel
+          </a>
+
+          <ConfirmButton
+            onClick={this.onDeleteConflicts.bind(this)}
+            text="Delete Revisions"
+            buttonType="btn-danger" />
+        </Modal.Footer>
+      </Modal>
+    );
+  }
+}
+ConfirmModal.propTypes = {
+  onConfirm: PropTypes.func.isRequired,
+};
+
+const BackForwardControls = ({onClick, forward}) => {
+  const icon = forward ? 'fonticon-right-open' : 'fonticon-left-open';
+  const style = {height: '20px', width: '11px', marginTop: '7px'};
+
+  return <div style={style} className={icon} onClick={onClick}></div>;
+};
+BackForwardControls.propTypes = {
+  onClick: PropTypes.func.isRequired,
+};
+
+export default {
+  DiffyController: DiffyController
+};
diff --git a/app/addons/documents/sidebar/sidebar.js 
b/app/addons/documents/sidebar/sidebar.js
index 90760bb..6ccc240 100644
--- a/app/addons/documents/sidebar/sidebar.js
+++ b/app/addons/documents/sidebar/sidebar.js
@@ -574,7 +574,7 @@ class CloneIndexModal extends React.Component {
   };
 
   submit = () => {
-    if (!this.refs.designDocSelector.validate()) {
+    if (!this.designDocSelector.validate()) {
       return;
     }
     if (this.props.newIndexName === '') {
@@ -615,7 +615,7 @@ class CloneIndexModal extends React.Component {
 
             <div className="row">
               <DesignDocSelector
-                ref="designDocSelector"
+                ref={node => this.designDocSelector = node}
                 designDocList={this.props.designDocArray}
                 selectedDesignDocName={this.props.selectedDesignDoc}
                 newDesignDocName={this.props.newDesignDocName}
diff --git a/app/addons/fauxton/notifications/notifications.js 
b/app/addons/fauxton/notifications/notifications.js
index 3e0f8f4..930bebb 100644
--- a/app/addons/fauxton/notifications/notifications.js
+++ b/app/addons/fauxton/notifications/notifications.js
@@ -282,7 +282,7 @@ class Notification extends React.Component {
         <div
           style={{opacity: style.opacity, minHeight: style.minHeight + 'px'}}
           className={'global-notification alert alert-' + this.props.type}
-          ref="notification">
+          ref={node => this.notification = node}>
           <a data-bypass href="#" onClick={this.hide}><i className="pull-right 
fonticon-cancel" /></a>
           <i className={'notification-icon ' + iconMap[this.props.type]} />
           <span dangerouslySetInnerHTML={this.getMsg()}></span>
diff --git a/app/addons/replication/controller.js 
b/app/addons/replication/controller.js
index 3d312b1..6e96f0c 100644
--- a/app/addons/replication/controller.js
+++ b/app/addons/replication/controller.js
@@ -170,7 +170,6 @@ export default class ReplicationController extends 
React.Component {
         selectAllDocs={Actions.selectAllReplicates}
         allDocsSelected={allReplicateSelected}
         someDocsSelected={someReplicateSelected}
-        deleteDocs={Actions.deleteDocs}
         activitySort={activitySort}
         changeActivitySort={Actions.changeActivitySort}
         deleteDocs={Actions.deleteReplicates}
@@ -187,7 +186,6 @@ export default class ReplicationController extends 
React.Component {
       onFilterChange={Actions.filterDocs}
       selectAllDocs={Actions.selectAllDocs}
       selectDoc={Actions.selectDoc}
-      selectAllDocs={Actions.selectAllDocs}
       allDocsSelected={allDocsSelected}
       someDocsSelected={someDocsSelected}
       deleteDocs={Actions.deleteDocs}

-- 
To stop receiving notification emails like this one, please contact
"[email protected]" <[email protected]>.

Reply via email to