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 2016/02/23 19:19:19 UTC
fauxton commit: updated refs/heads/master to 37cfb74
Repository: couchdb-fauxton
Updated Branches:
refs/heads/master 40d956f07 -> 37cfb74bc
Make DesignDocSelector into a dumb component
The dumbifies the DesignDocSelector to remove all ties a to
store and passes everything via props so we can get more use out
of it. Specifically, I need this for my next PR which includes
a new, generic Clone Index modal that'll use this component
in different contexts (i.e. all index types).
- Includes validation in the component to make it more
self-contained and allows for better UX by focusing on error
fields.
- Save action significantly simplied to remove custom update
logic and always redirect to appropriate View page.
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/37cfb74b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/37cfb74b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/37cfb74b
Branch: refs/heads/master
Commit: 37cfb74bc3acf62e08e08a30c09bcdda98249b56
Parents: 40d956f
Author: Ben Keen <be...@gmail.com>
Authored: Fri Feb 19 11:05:04 2016 -0800
Committer: Ben Keen <be...@gmail.com>
Committed: Tue Feb 23 07:24:34 2016 -0800
----------------------------------------------------------------------
.../documents/assets/less/view-editor.less | 23 ++
app/addons/documents/index-editor/actions.js | 112 ++++-----
.../documents/index-editor/actiontypes.js | 1 +
.../documents/index-editor/components.react.jsx | 187 ++++++++-------
app/addons/documents/index-editor/stores.js | 66 ++++--
.../documents/index-editor/tests/actionsSpec.js | 208 +----------------
.../documents/index-editor/tests/storesSpec.js | 61 +++--
.../tests/viewIndex.componentsSpec.react.jsx | 229 +++++++++++--------
app/addons/documents/routes-index-editor.js | 3 +-
9 files changed, 388 insertions(+), 502 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/app/addons/documents/assets/less/view-editor.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/view-editor.less b/app/addons/documents/assets/less/view-editor.less
index 05b60ab..51bca44 100644
--- a/app/addons/documents/assets/less/view-editor.less
+++ b/app/addons/documents/assets/less/view-editor.less
@@ -217,3 +217,26 @@ body .view-query-save .control-group {
.spanX (@gridColumns);
}
}
+
+
+/* temporary CSS overrides. This will be removed once the Views is moved to the standard 2-panel layout */
+.define-view .new-ddoc-section {
+ .span5 {
+ .label {
+ display: none;
+ }
+ .control-label {
+ display: none;
+ }
+ }
+ .span3 span {
+ font-weight: bold;
+ }
+ #new-ddoc-section {
+ margin: 16px 0 0;
+ .controls {
+ margin-left: 0;
+ }
+ }
+}
+/* end temporary override */
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/app/addons/documents/index-editor/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/actions.js b/app/addons/documents/index-editor/actions.js
index 294b02d..552ca6f 100644
--- a/app/addons/documents/index-editor/actions.js
+++ b/app/addons/documents/index-editor/actions.js
@@ -18,25 +18,16 @@ define([
'addons/documents/index-results/actions'
],
function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions) {
- var ActionHelpers = {
- createNewDesignDoc: function (id, database) {
- var designDoc = {
- _id: id,
- views: {
- }
- };
-
- return new Documents.Doc(designDoc, {database: database});
- },
+ var ActionHelpers = {
findDesignDoc: function (designDocs, designDocId) {
return _.find(designDocs, function (doc) {
return doc.id === designDocId;
}).dDocModel();
-
}
};
+
return {
//helpers are added here for use in testing actions
helpers: ActionHelpers,
@@ -48,20 +39,6 @@ function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions) {
});
},
- newDesignDoc: function () {
- FauxtonAPI.dispatch({
- type: ActionTypes.NEW_DESIGN_DOC
- });
- },
-
- designDocChange: function (id, newDesignDoc) {
- FauxtonAPI.dispatch({
- type: ActionTypes.DESIGN_DOC_CHANGE,
- newDesignDoc: newDesignDoc,
- designDocId: id
- });
- },
-
changeViewName: function (name) {
FauxtonAPI.dispatch({
type: ActionTypes.VIEW_NAME_CHANGE,
@@ -87,58 +64,33 @@ function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions) {
},
saveView: function (viewInfo) {
- var designDoc;
- var designDocs = viewInfo.designDocs;
-
- if (_.isUndefined(viewInfo.designDocId)) {
- FauxtonAPI.addNotification({
- msg: "Please enter a design doc name.",
- type: "error",
- clear: true
- });
-
- return;
- }
-
- if (viewInfo.newDesignDoc) {
- designDoc = ActionHelpers.createNewDesignDoc(viewInfo.designDocId, viewInfo.database);
- } else {
- designDoc = ActionHelpers.findDesignDoc(designDocs, viewInfo.designDocId);
- }
-
- var result = designDoc.setDdocView(viewInfo.viewName,
- viewInfo.map,
- viewInfo.reduce);
+ var designDoc = viewInfo.designDoc;
+ designDoc.setDdocView(viewInfo.viewName, viewInfo.map, viewInfo.reduce);
- if (result) {
+ FauxtonAPI.addNotification({
+ msg: "Saving View...",
+ type: "info",
+ clear: true
+ });
+ designDoc.save().then(function () {
FauxtonAPI.addNotification({
- msg: "Saving View...",
- type: "info",
+ msg: "View Saved.",
+ type: "success",
clear: true
});
- designDoc.save().then(function () {
- FauxtonAPI.addNotification({
- msg: "View Saved.",
- type: "success",
- clear: true
- });
-
-
- if (_.any([viewInfo.designDocChanged, viewInfo.hasViewNameChanged, viewInfo.newDesignDoc, viewInfo.newView])) {
- FauxtonAPI.dispatch({
- type: ActionTypes.VIEW_SAVED
- });
- var fragment = FauxtonAPI.urls('view', 'showNewlySavedView', viewInfo.database.safeID(), designDoc.safeID(), app.utils.safeURLName(viewInfo.viewName));
- FauxtonAPI.navigate(fragment, {trigger: true});
- } else {
- this.updateDesignDoc(designDoc);
- }
-
- IndexResultsActions.reloadResultsList();
- }.bind(this));
- }
+ if (_.any([viewInfo.designDocChanged, viewInfo.hasViewNameChanged, viewInfo.newDesignDoc, viewInfo.newView])) {
+ FauxtonAPI.dispatch({ type: ActionTypes.VIEW_SAVED });
+ var fragment = FauxtonAPI.urls('view', 'showNewlySavedView', viewInfo.database.safeID(), designDoc.safeID(), app.utils.safeURLName(viewInfo.viewName));
+ FauxtonAPI.navigate(fragment, { trigger: true });
+ } else {
+ this.updateDesignDoc(designDoc);
+ }
+
+ // this can be removed after the Views are on their own page
+ IndexResultsActions.reloadResultsList();
+ }.bind(this));
},
updateDesignDoc: function (designDoc) {
@@ -181,6 +133,24 @@ function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions) {
type: ActionTypes.VIEW_UPDATE_REDUCE_CODE,
code: code
});
+ },
+
+ selectDesignDoc: function (designDoc) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.DESIGN_DOC_CHANGE,
+ options: {
+ value: designDoc
+ }
+ });
+ },
+
+ updateNewDesignDocName: function (designDocName) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.DESIGN_DOC_NEW_NAME_UPDATED,
+ options: {
+ value: designDocName
+ }
+ });
}
};
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/app/addons/documents/index-editor/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/actiontypes.js b/app/addons/documents/index-editor/actiontypes.js
index 43fe021..7104d31 100644
--- a/app/addons/documents/index-editor/actiontypes.js
+++ b/app/addons/documents/index-editor/actiontypes.js
@@ -19,6 +19,7 @@ define([], function () {
VIEW_SAVED: 'VIEW_SAVED',
VIEW_CREATED: 'VIEW_CREATED',
DESIGN_DOC_CHANGE: 'DESIGN_DOC_CHANGE',
+ DESIGN_DOC_NEW_NAME_UPDATED: 'DESIGN_DOC_NEW_NAME_UPDATED',
NEW_DESIGN_DOC: 'NEW_DESIGN_DOC',
VIEW_NAME_CHANGE: 'VIEW_NAME_CHANGE',
VIEW_UPDATE_DESIGN_DOC: 'VIEW_UPDATE_DESIGN_DOC',
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/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 2d9e710..7c40570 100644
--- a/app/addons/documents/index-editor/components.react.jsx
+++ b/app/addons/documents/index-editor/components.react.jsx
@@ -14,13 +14,14 @@ define([
'app',
'api',
'react',
+ 'react-dom',
'addons/documents/index-editor/stores',
'addons/documents/index-editor/actions',
'addons/fauxton/components',
'addons/components/react-components.react'
],
-function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents) {
+function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, ReactComponents) {
var indexEditorStore = Stores.indexEditorStore;
var getDocUrl = app.helpers.getDocUrl;
var StyledSelect = ReactComponents.StyledSelect;
@@ -29,111 +30,102 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
var ConfirmButton = ReactComponents.ConfirmButton;
var LoadLines = ReactComponents.LoadLines;
+
var DesignDocSelector = React.createClass({
+ propTypes: {
+ designDocList: React.PropTypes.array.isRequired,
+ onSelectDesignDoc: React.PropTypes.func.isRequired,
+ onChangeNewDesignDocName: React.PropTypes.func.isRequired,
+ selectedDesignDocName: React.PropTypes.string.isRequired,
+ newDesignDocName: React.PropTypes.string.isRequired,
+ designDocLabel: React.PropTypes.string,
+ docURL: React.PropTypes.string
+ },
- getStoreState: function () {
+ getDefaultProps: function () {
return {
- designDocId: indexEditorStore.getDesignDocId(),
- designDocs: indexEditorStore.getDesignDocs(),
- newDesignDoc: indexEditorStore.isNewDesignDoc()
+ designDocLabel: 'Design Document'
};
},
- getInitialState: function () {
- return this.getStoreState();
+ validate: function () {
+ if (this.props.selectedDesignDocName === 'new-doc' && this.props.newDesignDocName === '') {
+ FauxtonAPI.addNotification({
+ msg: 'Please name your design doc.',
+ type: 'error'
+ });
+ ReactDOM.findDOMNode(this.refs.newDesignDoc).focus();
+ return false;
+ }
+ return true;
},
- getNewDesignDocInput: function () {
- return (
- <div className="new-ddoc-section">
- <div className="new-ddoc-input">
- <input value={this.state.designDoc} type="text" id="new-ddoc" onChange={this.onDesignDocChange} placeholder="Name" />
- </div>
- </div>
- );
+ getDocList: function () {
+ return _.map(this.props.designDocList, function (designDoc) {
+ return (<option key={designDoc} value={designDoc}>{designDoc}</option>);
+ });
},
- onDesignDocChange: function (event) {
- Actions.designDocChange('_design/' + event.target.value, true);
+ selectDesignDoc: function (e) {
+ this.props.onSelectDesignDoc(e.target.value);
},
- getDesignDocOptions: function () {
- return this.state.designDocs.map(function (doc, i) {
- return <option key={i} value={doc.id}>{doc.id}</option>;
- });
+ updateDesignDocName: function (e) {
+ this.props.onChangeNewDesignDocName(e.target.value);
},
- getSelectContent: function () {
- var designDocOptions = this.getDesignDocOptions();
-
+ getNewDDocField: function () {
+ if (this.props.selectedDesignDocName !== 'new-doc') {
+ return;
+ }
return (
- <optgroup label="Select a document">
- <option value="new">New Design Document</option>
- {designDocOptions}
- </optgroup>
+ <div id="new-ddoc-section" className="span5">
+ <label className="control-label" htmlFor="new-ddoc">_design/</label>
+ <div className="controls">
+ <input type="text" ref="newDesignDoc" id="new-ddoc" placeholder="newDesignDoc"
+ onChange={this.updateDesignDocName}/>
+ </div>
+ </div>
);
},
- render: function () {
- var designDocInput;
- var designDocId = this.state.designDocId;
-
- if (this.state.newDesignDoc) {
- designDocInput = this.getNewDesignDocInput();
- designDocId = 'new';
+ getDocLink: function () {
+ if (!this.props.docLink) {
+ return null;
}
+ return (
+ <a className="help-link" data-bypass="true" href={this.props.docLink} target="_blank">
+ <i className="icon-question-sign" />
+ </a>
+ );
+ },
+ render: function () {
return (
- <div className="new-ddoc-section">
- <PaddedBorderedBox>
- <div className="control-group design-doc-group">
- <div className="pull-left">
- <label htmlFor="ddoc"><strong>Design Document</strong>
- <a className="help-link" data-bypass="true" href={getDocUrl('DESIGN_DOCS')} target="_blank">
- <i className="icon-question-sign">
- </i>
- </a>
- </label>
- <StyledSelect
- selectContent={this.getSelectContent()}
- selectChange={this.selectChange}
- selectId="ddoc"
- selectValue={designDocId}
- />
- </div>
- <div className="pull-left">
- {designDocInput}
- </div>
+ <div className="design-doc-group control-group">
+ <div className="span3">
+ <label htmlFor="ddoc">{this.props.designDocLabel}
+ {this.getDocLink()}
+ </label>
+ <div className="styled-select">
+ <label htmlFor="js-backup-list-select">
+ <i className="fonticon-down-dir" />
+ <select id="ddoc" onChange={this.selectDesignDoc} value={this.props.selectedDesignDocName}>
+ <optgroup label="Select a document">
+ <option value="new-doc">New document</option>
+ {this.getDocList()}
+ </optgroup>
+ </select>
+ </label>
</div>
- </PaddedBorderedBox>
+ </div>
+ {this.getNewDDocField()}
</div>
);
- },
-
- selectChange: function (event) {
- var designDocId = event.target.value;
-
- if (designDocId === 'new') {
- Actions.newDesignDoc();
- } else {
- Actions.designDocChange(designDocId, false);
- }
- },
-
- onChange: function () {
- this.setState(this.getStoreState());
- },
-
- componentDidMount: function () {
- indexEditorStore.on('change', this.onChange, this);
- },
-
- componentWillUnmount: function () {
- indexEditorStore.off('change', this.onChange);
}
-
});
+
var ReduceEditor = React.createClass({
getStoreState: function () {
@@ -277,6 +269,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
});
var Editor = React.createClass({
+
getStoreState: function () {
return {
hasViewNameChanged: indexEditorStore.hasViewNameChanged(),
@@ -284,9 +277,12 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
isNewView: indexEditorStore.isNewView(),
viewName: indexEditorStore.getViewName(),
designDocs: indexEditorStore.getDesignDocs(),
+ designDocList: indexEditorStore.getAvailableDesignDocs(),
hasDesignDocChanged: indexEditorStore.hasDesignDocChanged(),
newDesignDoc: indexEditorStore.isNewDesignDoc(),
designDocId: indexEditorStore.getDesignDocId(),
+ newDesignDocName: indexEditorStore.getNewDesignDocName(),
+ saveDesignDoc: indexEditorStore.getSaveDesignDoc(),
map: indexEditorStore.getMap(),
isLoading: indexEditorStore.isLoading()
};
@@ -297,7 +293,9 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
},
onChange: function () {
- this.setState(this.getStoreState());
+ if (this.isMounted()) {
+ this.setState(this.getStoreState());
+ }
},
componentDidMount: function () {
@@ -314,8 +312,12 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
return mapEditorErrors || customReduceErrors;
},
- saveView: function (event) {
- event.preventDefault();
+ saveView: function (e) {
+ e.preventDefault();
+
+ if (!this.refs.designDocSelector.validate()) {
+ return;
+ }
if (this.hasErrors()) {
FauxtonAPI.addNotification({
@@ -330,6 +332,7 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
database: this.state.database,
newView: this.state.isNewView,
viewName: this.state.viewName,
+ designDoc: this.state.saveDesignDoc,
designDocId: this.state.designDocId,
newDesignDoc: this.state.newDesignDoc,
designDocChanged: this.state.hasDesignDocChanged,
@@ -340,8 +343,8 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
});
},
- viewChange: function (event) {
- Actions.changeViewName(event.target.value);
+ viewChange: function (e) {
+ Actions.changeViewName(e.target.value);
},
updateMapCode: function (code) {
@@ -358,7 +361,6 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
}
var url = '#/' + FauxtonAPI.urls('allDocs', 'app', this.state.database.id, '');
-
return (
<div className="define-view">
<PaddedBorderedBox>
@@ -371,7 +373,20 @@ function (app, FauxtonAPI, React, Stores, Actions, Components, ReactComponents)
</div>
</PaddedBorderedBox>
<form className="form-horizontal view-query-save" onSubmit={this.saveView}>
- <DesignDocSelector />
+
+ <div className="new-ddoc-section">
+ <PaddedBorderedBox>
+ <DesignDocSelector
+ ref="designDocSelector"
+ designDocList={this.state.designDocList}
+ selectedDesignDocName={this.state.designDocId}
+ newDesignDocName={this.state.newDesignDocName}
+ onSelectDesignDoc={Actions.selectDesignDoc}
+ onChangeNewDesignDocName={Actions.updateNewDesignDocName}
+ docLink={getDocUrl('DESIGN_DOCS')} />
+ </PaddedBorderedBox>
+ </div>
+
<div className="control-group">
<PaddedBorderedBox>
<label htmlFor="index-name">
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/app/addons/documents/index-editor/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/stores.js b/app/addons/documents/index-editor/stores.js
index 9615451..ebc37ae 100644
--- a/app/addons/documents/index-editor/stores.js
+++ b/app/addons/documents/index-editor/stores.js
@@ -12,10 +12,11 @@
define([
'api',
- 'addons/documents/index-editor/actiontypes'
+ 'addons/documents/index-editor/actiontypes',
+ 'addons/documents/resources'
],
-function (FauxtonAPI, ActionTypes) {
+function (FauxtonAPI, ActionTypes, Resources) {
var Stores = {};
Stores.IndexEditorStore = FauxtonAPI.Store.extend({
@@ -37,8 +38,9 @@ function (FauxtonAPI, ActionTypes) {
editIndex: function (options) {
this._database = options.database;
this._newView = options.newView;
- this._newDesignDoc = options.newDesignDoc || false;
this._viewName = options.viewName || 'viewName';
+ this._newDesignDoc = options.newDesignDoc || false;
+ this._newDesignDocName = '';
this._designDocs = options.designDocs;
this._designDocId = options.designDocId;
this._designDocChanged = false;
@@ -83,7 +85,6 @@ function (FauxtonAPI, ActionTypes) {
return this._designDocs.find(function (ddoc) {
return this._designDocId == ddoc.id;
}, this).dDocModel();
-
},
getDesignDocs: function () {
@@ -92,14 +93,19 @@ function (FauxtonAPI, ActionTypes) {
});
},
+ // returns a simple array of design doc IDs
+ getAvailableDesignDocs: function () {
+ return _.map(this.getDesignDocs(), function (doc) {
+ return doc.id;
+ });
+ },
+
getDesignDocId: function () {
return this._designDocId;
},
- setDesignDocId: function (designDocId, newDesignDoc) {
+ setDesignDocId: function (designDocId) {
this._designDocId = designDocId;
- this._newDesignDoc = newDesignDoc;
- this._designDocChanged = true;
},
hasDesignDocChanged: function () {
@@ -177,6 +183,27 @@ function (FauxtonAPI, ActionTypes) {
this._designDocs.add(designDoc, {merge: true});
},
+ getNewDesignDocName: function () {
+ return this._newDesignDocName;
+ },
+
+ getSaveDesignDoc: function () {
+ if (this._designDocId === 'new-doc') {
+ var doc = {
+ _id: '_design/' + this._newDesignDocName,
+ views: {},
+ language: 'javascript'
+ };
+ return new Resources.Doc(doc, { database: this._database });
+ }
+
+ var foundDoc = this._designDocs.find(function (ddoc) {
+ return ddoc.id === this._designDocId;
+ }.bind(this));
+
+ return (!foundDoc) ? null : foundDoc.dDocModel();
+ },
+
dispatch: function (action) {
switch (action.type) {
case ActionTypes.CLEAR_INDEX:
@@ -185,68 +212,57 @@ function (FauxtonAPI, ActionTypes) {
case ActionTypes.EDIT_INDEX:
this.editIndex(action.options);
- this.triggerChange();
break;
case ActionTypes.VIEW_NAME_CHANGE:
this.setViewName(action.name);
- this.triggerChange();
break;
case ActionTypes.EDIT_NEW_INDEX:
this.editIndex(action.options);
- this.triggerChange();
break;
case ActionTypes.SELECT_REDUCE_CHANGE:
this.updateReduceFromSelect(action.reduceSelectedOption);
- this.triggerChange();
break;
case ActionTypes.DESIGN_DOC_CHANGE:
- this.setDesignDocId(action.designDocId, action.newDesignDoc);
- this.triggerChange();
- break;
-
- case ActionTypes.NEW_DESIGN_DOC:
- this.setDesignDocId('', true);
- this.triggerChange();
+ this.setDesignDocId(action.options.value);
break;
case ActionTypes.VIEW_SAVED:
- this.triggerChange();
break;
case ActionTypes.VIEW_CREATED:
- this.triggerChange();
break;
case ActionTypes.VIEW_UPDATE_DESIGN_DOC:
this.updateDesignDoc(action.designDoc);
this.setView();
- this.triggerChange();
break;
case ActionTypes.VIEW_UPDATE_MAP_CODE:
this.setMap(action.code);
- this.triggerChange();
break;
case ActionTypes.VIEW_UPDATE_REDUCE_CODE:
this.setReduce(action.code);
- this.triggerChange();
+ break;
+
+ case ActionTypes.DESIGN_DOC_NEW_NAME_UPDATED:
+ this._newDesignDocName = action.options.value;
break;
default:
return;
- // do nothing
}
+
+ this.triggerChange();
}
});
Stores.indexEditorStore = new Stores.IndexEditorStore();
-
Stores.indexEditorStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.indexEditorStore.dispatch);
return Stores;
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/app/addons/documents/index-editor/tests/actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/tests/actionsSpec.js b/app/addons/documents/index-editor/tests/actionsSpec.js
index 9461fd6..8064962 100644
--- a/app/addons/documents/index-editor/tests/actionsSpec.js
+++ b/app/addons/documents/index-editor/tests/actionsSpec.js
@@ -25,208 +25,12 @@ define([
FauxtonAPI.router = new FauxtonAPI.Router([]);
+
describe('Index Editor Actions', function () {
var database = {
- safeID: function () { return 'id';}
+ safeID: function () { return 'id'; }
};
- describe('save view', function () {
- var designDoc, designDocs;
- beforeEach(function () {
- designDoc = {
- _id: '_design/test-doc',
- _rev: '1-231313',
- views: {
- 'test-view': {
- map: 'function () {};',
- }
- }
- };
- var doc = new Documents.Doc(designDoc, {database: database});
- designDocs = new Documents.AllDocs([doc], {
- params: { limit: 10 },
- database: database
- });
-
- designDocs = designDocs.models;
- });
-
- afterEach(function () {
- restore(FauxtonAPI.navigate);
- restore(FauxtonAPI.triggerRouteEvent);
- restore(IndexResultsActions.reloadResultsList);
- restore(Actions.updateDesignDoc);
- });
-
- it('shows a notification if no design doc id given', function () {
- var spy = sinon.spy(FauxtonAPI, 'addNotification');
-
- var viewInfo = {
- database: database,
- viewName: 'new-doc',
- designDocId: undefined,
- map: 'map',
- reduce: '_sum',
- newDesignDoc: true,
- newView: true,
- designDocs: designDocs
- };
-
- Actions.saveView(viewInfo);
- assert.ok(spy.calledOnce);
- FauxtonAPI.addNotification.restore();
- });
-
- it('creates new design Doc for new design doc', function () {
- var spy = sinon.spy(Actions.helpers, 'createNewDesignDoc');
-
- var viewInfo = {
- database: database,
- viewName: 'new-doc',
- designDocId: '_design/test-doc',
- map: 'map',
- reduce: '_sum',
- newDesignDoc: true,
- newView: true,
- designDocs: designDocs
- };
-
- Actions.saveView(viewInfo);
- assert.ok(spy.calledOnce);
- });
-
- it('sets the design doc with updated view', function () {
- var viewInfo = {
- viewName: 'test-view',
- designDocId: '_design/test-doc',
- map: 'map',
- reduce: '_sum',
- newDesignDoc: false,
- newView: true,
- designDocs: designDocs
- };
-
- Actions.saveView(viewInfo);
-
- var updatedDesignDoc = _.first(designDocs).dDocModel();
- assert.equal(updatedDesignDoc.get('views')['test-view'].reduce, '_sum');
- });
-
- it('saves doc', function () {
- var viewInfo = {
- viewName: 'test-view',
- designDocId: '_design/test-doc',
- map: 'map',
- reduce: '_sum',
- newDesignDoc: false,
- newView: true,
- designDocs: designDocs
- };
-
- var updatedDesignDoc = _.first(designDocs).dDocModel();
- var spy = sinon.spy(updatedDesignDoc, 'save');
- Actions.saveView(viewInfo);
-
- assert.ok(spy.calledOnce);
- });
-
- it('updates design doc', function () {
- var viewInfo = {
- viewName: 'test-view',
- designDocId: '_design/test-doc',
- map: 'map',
- reduce: '_sum',
- newDesignDoc: false,
- newView: false,
- designDocs: designDocs,
- database: {
- safeID: function () { return '1';}
- }
- };
-
- designDocs.find = function () {};
- designDocs.add = function () {};
- designDocs.dDocModel = function () {};
-
- Actions.editIndex({
- database: {id: 'rockos-db'},
- newView: true,
- viewName: 'test-view',
- designDocs: designDocs,
- designDocId: designDocs[0]._id
- });
-
- var promise = FauxtonAPI.Deferred();
- promise.resolve();
-
- var updatedDesignDoc = _.first(designDocs).dDocModel();
- var stub = sinon.stub(updatedDesignDoc, 'save');
- stub.returns(promise);
-
- var spy = sinon.spy(Actions, 'updateDesignDoc');
- Actions.saveView(viewInfo);
-
- assert.ok(spy.calledOnce);
- });
-
- it('navigates to new url for new view', function () {
- var spy = sinon.spy(FauxtonAPI, 'navigate');
-
- var viewInfo = {
- database: database,
- viewName: 'test-view',
- designDocId: '_design/test-doc',
- map: 'map',
- reduce: '_sum',
- newDesignDoc: false,
- newView: true,
- designDocs: designDocs
- };
- var designDoc = _.first(designDocs);
-
- designDoc.save = function () {
- var promise = $.Deferred();
- promise.resolve();
- return promise;
- };
-
- Actions.saveView(viewInfo);
- assert.ok(spy.calledOnce);
- assert.ok(spy.getCall(0).args[0].match(/_view\/test-view/));
- });
-
- it('triggers reload results list', function () {
- var spy = sinon.spy(IndexResultsActions, 'reloadResultsList');
-
- var viewInfo = {
- viewName: 'test-view',
- designDocId: '_design/test-doc',
- map: 'map',
- reduce: '_sum',
- newDesignDoc: false,
- newView: false,
- designDocs: designDocs,
- database: {
- safeID: function () {
- return 'foo';
- }
- }
- };
- var designDoc = _.first(designDocs);
-
- designDoc.save = function () {
- var promise = $.Deferred();
- promise.resolve();
- return promise;
- };
-
- var stub = sinon.stub(Actions, 'updateDesignDoc');
- stub.returns(true);
-
- Actions.saveView(viewInfo);
- assert.ok(spy.calledOnce);
- });
- });
describe('delete view', function () {
var designDocs, database, designDoc, designDocId, viewName;
@@ -242,10 +46,10 @@ define([
_rev: '1-231',
views: {
'test-view': {
- map: 'function () {};',
+ map: 'function () {};'
},
'test-view2': {
- map: 'function () {};',
+ map: 'function () {};'
}
}
}], {
@@ -254,7 +58,6 @@ define([
});
designDocs = designDocs.models;
designDoc = _.first(designDocs);
-
});
afterEach(function () {
@@ -299,7 +102,6 @@ define([
});
assert.ok(spy.calledOnce);
-
});
it('navigates to all docs', function () {
@@ -318,7 +120,6 @@ define([
designDocs: designDocs
});
-
assert.ok(spy.getCall(0).args[0].match(/_all_docs/));
assert.ok(spy.calledOnce);
});
@@ -345,4 +146,5 @@ define([
});
});
+
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/app/addons/documents/index-editor/tests/storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/tests/storesSpec.js b/app/addons/documents/index-editor/tests/storesSpec.js
index c60295c..db098a6 100644
--- a/app/addons/documents/index-editor/tests/storesSpec.js
+++ b/app/addons/documents/index-editor/tests/storesSpec.js
@@ -222,10 +222,43 @@ define([
}
};
- var designDocs = new Documents.AllDocs([designDoc], {
+ var mangoDoc = {
+ "_id": "_design/123mango",
+ "id": "_design/123mango",
+ "key": "_design/123mango",
+ "value": {
+ "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
+ },
+ "doc": {
+ "_id": "_design/123mango",
+ "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
+ "views": {
+ "test-view": {
+ "map": "function(doc) {\n emit(doc._id, 2);\n}"
+ },
+ "new-view": {
+ "map": "function(doc) {\n if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n emit(doc._id, 1);\n}",
+ "reduce": "_sum"
+ }
+ },
+ "language": "query",
+ "indexes": {
+ "newSearch": {
+ "analyzer": "standard",
+ "index": "function(doc){\n index(\"default\", doc._id);\n}"
+ }
+ }
+ }
+ };
+
+ var designDocArray = _.map([designDoc, mangoDoc], function (doc) {
+ return Documents.Doc.prototype.parse(doc);
+ });
+
+ var designDocs = new Documents.AllDocs(designDocArray, {
params: { limit: 10 },
database: {
- safeID: function () { return 'id';}
+ safeID: function () { return 'id'; }
}
});
@@ -240,25 +273,25 @@ define([
});
});
+ afterEach(function () {
+ store.reset();
+ });
+
it('DESIGN_DOC_CHANGE changes design doc id', function () {
- var designDocId = 'another-one';
+ var designDocId = 'another-one';
FauxtonAPI.dispatch({
type: ActionTypes.DESIGN_DOC_CHANGE,
- designDocId: designDocId,
- newDesignDoc: false
+ options: {
+ value: designDocId
+ }
});
-
assert.equal(store.getDesignDocId(), designDocId);
- assert.notOk(store.isNewDesignDoc());
});
- it('sets new design doc on NEW_DESIGN_DOC', function () {
- FauxtonAPI.dispatch({
- type: ActionTypes.NEW_DESIGN_DOC
- });
-
- assert.ok(store.isNewDesignDoc());
- assert.equal(store.getDesignDocId(), '');
+ it('only filters mango docs', function () {
+ var designDocs = store.getDesignDocs();
+ assert.equal(designDocs.length, 1);
+ assert.equal(designDocs[0].id, '_design/test-doc');
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/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 e217d3c..51ed2a2 100644
--- a/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
+++ b/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
@@ -11,41 +11,44 @@
// the License.
define([
'api',
+ 'addons/documents/resources',
'addons/documents/index-editor/components.react',
'addons/documents/index-editor/stores',
'addons/documents/index-editor/actions',
- 'addons/documents/resources',
'testUtils',
"react",
'react-dom'
-], function (FauxtonAPI, Views, Stores, Actions, Documents, utils, React, ReactDOM) {
+], function (FauxtonAPI, Resources, Views, Stores, Actions, utils, React, ReactDOM) {
FauxtonAPI.router = new FauxtonAPI.Router([]);
var assert = utils.assert;
var TestUtils = React.addons.TestUtils;
- var restore = utils.restore;
+
var resetStore = function (designDocs) {
+ Actions.editIndex({
+ database: { id: 'rockos-db' },
+ newView: false,
+ viewName: 'test-view',
+ designDocs: getDesignDocsCollection(designDocs),
+ designDocId: designDocs[0]._id
+ });
+ };
+
+ var getDesignDocsCollection = function (designDocs) {
designDocs = designDocs.map(function (doc) {
- return Documents.Doc.prototype.parse(doc);
+ return Resources.Doc.prototype.parse(doc);
});
- var ddocs = new Documents.AllDocs(designDocs, {
+ return new Resources.AllDocs(designDocs, {
params: { limit: 10 },
database: {
- safeID: function () { return 'id';}
+ safeID: function () { return 'id'; }
}
});
-
- Actions.editIndex({
- database: {id: 'rockos-db'},
- newView: false,
- viewName: 'test-view',
- designDocs: ddocs,
- designDocId: designDocs[0]._id
- });
};
+
describe('reduce editor', function () {
var container, reduceEl;
@@ -70,8 +73,7 @@ define([
_id: '_design/test-doc',
views: {
'test-view': {
- map: 'function () {};',
- //reduce: 'function (reduce) { reduce(); }'
+ map: 'function () {};'
}
}
};
@@ -102,121 +104,146 @@ define([
});
});
- describe('design Doc Selector', function () {
+ describe('DesignDocSelector component', function () {
var container, selectorEl;
-
- beforeEach(function () {
- container = document.createElement('div');
-
- var designDoc = {
- "id": "_design/test-doc",
- "key": "_design/test-doc",
- "value": {
- "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
- },
- "doc": {
- "_id": "_design/test-doc",
- "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
- "views": {
- "test-view": {
- "map": "function(doc) {\n emit(doc._id, 2);\n}"
- },
- "new-view": {
- "map": "function(doc) {\n if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n emit(doc._id, 1);\n}",
- "reduce": "_sum"
- }
+ var database = { id: 'db' };
+ var designDoc = {
+ "id": "_design/test-doc",
+ "key": "_design/test-doc",
+ "value": {
+ "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
+ },
+ "doc": {
+ "_id": "_design/test-doc",
+ "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
+ "views": {
+ "test-view": {
+ "map": "function(doc) {\n emit(doc._id, 2);\n}"
},
- "language": "javascript",
- "indexes": {
- "newSearch": {
- "analyzer": "standard",
- "index": "function(doc){\n index(\"default\", doc._id);\n}"
- }
+ "new-view": {
+ "map": "function(doc) {\n if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n emit(doc._id, 1);\n}",
+ "reduce": "_sum"
}
- }
- };
- var mangodoc = {
- "id": "_design/123mango",
- "key": "_design/123mango",
- "value": {
- "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
},
- "doc": {
- "_id": "_design/123mango",
- "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
- "views": {
- "test-view": {
- "map": "function(doc) {\n emit(doc._id, 2);\n}"
- },
- "new-view": {
- "map": "function(doc) {\n if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n emit(doc._id, 1);\n}",
- "reduce": "_sum"
- }
- },
- "language": "query",
- "indexes": {
- "newSearch": {
- "analyzer": "standard",
- "index": "function(doc){\n index(\"default\", doc._id);\n}"
- }
+ "language": "javascript",
+ "indexes": {
+ "newSearch": {
+ "analyzer": "standard",
+ "index": "function(doc){\n index(\"default\", doc._id);\n}"
}
}
- };
- resetStore([designDoc, mangodoc]);
- selectorEl = TestUtils.renderIntoDocument(<Views.DesignDocSelector/>, container);
- });
+ }
+ };
+ beforeEach(function () {
+ container = document.createElement('div');
+ });
afterEach(function () {
- restore(Actions.newDesignDoc);
- restore(Actions.designDocChange);
ReactDOM.unmountComponentAtNode(container);
});
- it('calls new design doc on new selected', function () {
- var spy = sinon.spy(Actions, 'newDesignDoc');
+
+ it('calls onSelectDesignDoc on change', function () {
+ var spy = sinon.spy();
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={getDesignDocsCollection([designDoc])}
+ selectedDDocName={'new-doc'}
+ onSelectDesignDoc={spy}
+ />, container);
+
TestUtils.Simulate.change($(ReactDOM.findDOMNode(selectorEl)).find('#ddoc')[0], {
target: {
- value: 'new'
+ value: '_design/test-doc'
}
});
-
assert.ok(spy.calledOnce);
});
- it('calls design doc changed on a different design doc selected', function () {
- var spy = sinon.spy(Actions, 'designDocChange');
- TestUtils.Simulate.change($(ReactDOM.findDOMNode(selectorEl)).find('#ddoc')[0], {
- target: {
- value: 'another-doc'
- }
- });
+ it('shows new design doc field when set to new-doc', function () {
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={['_design/test-doc']}
+ selectedDesignDocName={'new-doc'}
+ onSelectDesignDoc={function () { }}
+ />, container);
- assert.ok(spy.calledWith('another-doc', false));
+ assert.equal($(ReactDOM.findDOMNode(selectorEl)).find('#new-ddoc-section').length, 1);
});
- it('calls design doc changed on new design doc entered', function () {
- var spy = sinon.spy(Actions, 'designDocChange');
- Actions.newDesignDoc();
- TestUtils.Simulate.change($(ReactDOM.findDOMNode(selectorEl)).find('#new-ddoc')[0], {
- target: {
- value: 'new-doc-entered'
- }
- });
+ it('hides new design doc field when design doc selected', function () {
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={['_design/test-doc']}
+ selectedDesignDocName={'_design/test-doc'}
+ onSelectDesignDoc={function () { }}
+ />, container);
+
+ assert.equal($(ReactDOM.findDOMNode(selectorEl)).find('#new-ddoc-section').length, 0);
+ });
+
+ it('always passes validation when design doc selected', function () {
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={['_design/test-doc']}
+ selectedDesignDocName={'_design/test-doc'}
+ onSelectDesignDoc={function () { }}
+ />, container);
- assert.ok(spy.calledWith('_design/new-doc-entered', true));
+ assert.equal(selectorEl.validate(), true);
});
- it('does not filter usual design docs', function () {
- assert.ok(/_design\/test-doc/.test($(ReactDOM.findDOMNode(selectorEl)).text()));
+ it('fails validation if new doc name entered/not entered', function () {
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={['_design/test-doc']}
+ selectedDesignDocName={'new-doc'}
+ newDesignDocName=''
+ onSelectDesignDoc={function () { }}
+ />, container);
+
+ // it shouldn't validate at this point: no new design doc name has been entered
+ assert.equal(selectorEl.validate(), false);
});
- it('filters mango docs', function () {
- selectorEl = TestUtils.renderIntoDocument(<Views.DesignDocSelector/>, container);
- assert.notOk(/_design\/123mango/.test($(ReactDOM.findDOMNode(selectorEl)).text()));
+ it('passes validation if new doc name entered/not entered', function () {
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={['_design/test-doc']}
+ selectedDesignDocName={'new-doc'}
+ newDesignDocName='new-doc-name'
+ onSelectDesignDoc={function () { }}
+ />, container);
+ assert.equal(selectorEl.validate(), true);
+ });
+
+
+ it('omits doc URL when not supplied', function () {
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={['_design/test-doc']}
+ selectedDesignDocName={'new-doc'}
+ onSelectDesignDoc={function () { }}
+ />, container);
+ assert.equal($(ReactDOM.findDOMNode(selectorEl)).find('.help-link').length, 0);
+ });
+
+ it('includes help doc link when supplied', function () {
+ var docLink = 'http://docs.com';
+ selectorEl = TestUtils.renderIntoDocument(
+ <Views.DesignDocSelector
+ designDocList={['_design/test-doc']}
+ selectedDesignDocName={'new-doc'}
+ onSelectDesignDoc={function () { }}
+ docLink={docLink}
+ />, container);
+ assert.equal($(ReactDOM.findDOMNode(selectorEl)).find('.help-link').length, 1);
+ assert.equal($(ReactDOM.findDOMNode(selectorEl)).find('.help-link').attr('href'), docLink);
});
});
+
describe('Editor', function () {
var container, editorEl, sandbox;
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/37cfb74b/app/addons/documents/routes-index-editor.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-index-editor.js b/app/addons/documents/routes-index-editor.js
index 8d94a39..3a66cbd 100644
--- a/app/addons/documents/routes-index-editor.js
+++ b/app/addons/documents/routes-index-editor.js
@@ -122,9 +122,8 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Documents, IndexEditorComponents,
},
newViewEditor: function (database, _designDoc) {
- var params = app.getParams();
var newDesignDoc = true;
- var designDoc;
+ var designDoc = 'new-doc';
if (_designDoc) {
designDoc = '_design/' + _designDoc;