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/10/14 16:42:43 UTC
fauxton commit: updated refs/heads/master to 087e23f
Repository: couchdb-fauxton
Updated Branches:
refs/heads/master 6542a6928 -> 087e23fd4
Update to pass updates to API Bar via actions
The previous code used props on page load to set the API Bar
values (endpoint + doc URL). The problem was that because there
was no way to reference the APIBarController, it prohibited
on-the-fly updates to change the values without a route change.
This just tweaks it to store the API Bar data in a store that
can be updated by publishing an action. This will ultimately
allow us to do away with hacks like this:
https://github.com/apache/couchdb-fauxton/blob/master/app/addons/documents/header/header.react.jsx#L174
e.g. we could expand on this ticket and publish an
APIBAR_FADE_OUT” msg and the component would handle its own
fading out, rather than relying on other components directly
manipulating its DOM.
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/087e23fd
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/087e23fd
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/087e23fd
Branch: refs/heads/master
Commit: 087e23fd4e8237d3abdce47e8b7caed8ce95ed34
Parents: 6542a69
Author: Ben Keen <be...@gmail.com>
Authored: Fri Oct 9 12:04:15 2015 -0700
Committer: Ben Keen <be...@gmail.com>
Committed: Tue Oct 13 10:53:32 2015 -0700
----------------------------------------------------------------------
app/addons/components/actions.js | 45 +++++++
app/addons/components/actiontypes.js | 21 +++
.../components/react-components.react.jsx | 58 ++++++--
app/addons/components/stores.js | 87 ++++++++++++
.../tests/apiBarControllerSpec.react.jsx | 134 +++++++++++++++++++
app/addons/components/tests/storesSpec.js | 68 ++++++++++
app/addons/fauxton/base.js | 15 ++-
7 files changed, 412 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/087e23fd/app/addons/components/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/components/actions.js b/app/addons/components/actions.js
new file mode 100644
index 0000000..390877c
--- /dev/null
+++ b/app/addons/components/actions.js
@@ -0,0 +1,45 @@
+// 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/actiontypes'
+],
+function (FauxtonAPI, ActionTypes) {
+
+ function showAPIBar () {
+ FauxtonAPI.dispatch({ type: ActionTypes.SHOW_API_BAR });
+ }
+
+ function hideAPIBar () {
+ FauxtonAPI.dispatch({ type: ActionTypes.HIDE_API_BAR });
+ }
+
+ // general usage for setting multiple params at once. If a param isn't passed, it's not overridden
+ function updateAPIBar (params) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.UPDATE_API_BAR,
+ options: {
+ visible: params.visible,
+ endpoint: params.endpoint,
+ docURL: params.docURL
+ }
+ });
+ }
+
+ return {
+ showAPIBar: showAPIBar,
+ hideAPIBar: hideAPIBar,
+ updateAPIBar: updateAPIBar
+ };
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/087e23fd/app/addons/components/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/components/actiontypes.js b/app/addons/components/actiontypes.js
new file mode 100644
index 0000000..c8d8f80
--- /dev/null
+++ b/app/addons/components/actiontypes.js
@@ -0,0 +1,21 @@
+// 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([], function () {
+
+ return {
+ SHOW_API_BAR: 'SHOW_API_BAR',
+ HIDE_API_BAR: 'HIDE_API_BAR',
+ UPDATE_API_BAR: 'UPDATE_API_BAR'
+ };
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/087e23fd/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 41e49d2..95c5bfc 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -14,13 +14,16 @@ define([
'app',
'api',
'react',
+ 'addons/components/stores',
'addons/fauxton/components.react',
'ace/ace',
'plugins/beautify'
],
-function (app, FauxtonAPI, React, FauxtonComponents, ace, beautifyHelper) {
+function (app, FauxtonAPI, React, Stores, FauxtonComponents, ace, beautifyHelper) {
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
+ var componentStore = Stores.componentStore;
+
var ToggleHeaderButton = React.createClass({
getDefaultProps: function () {
@@ -1076,13 +1079,32 @@ function (app, FauxtonAPI, React, FauxtonComponents, ace, beautifyHelper) {
var ApiBarController = React.createClass({
- getDefaultProps: function () {
+ getInitialState: function () {
+ return this.getStoreState();
+ },
+
+ getStoreState: function () {
return {
- endpoint: '_all_docs',
- documentation: FauxtonAPI.constants.DOC_URLS.GENERAL
+ visible: componentStore.isAPIBarVisible(),
+ endpoint: componentStore.getEndpoint(),
+ docURL: componentStore.getDocURL()
};
},
+ onChange: function () {
+ if (this.isMounted()) {
+ this.setState(this.getStoreState());
+ }
+ },
+
+ componentDidMount: function () {
+ componentStore.on('change', this.onChange, this);
+ },
+
+ componentWillUnmount: function () {
+ componentStore.off('change', this.onChange);
+ },
+
showCopiedMessage: function () {
FauxtonAPI.addNotification({
msg: 'The API URL has been copied to the clipboard.',
@@ -1091,7 +1113,22 @@ function (app, FauxtonAPI, React, FauxtonComponents, ace, beautifyHelper) {
});
},
+ getDocIcon: function () {
+ if (!this.state.docURL) {
+ return false;
+ }
+ return (
+ <a className="help-link" data-bypass="true" href={this.state.docURL} target="_blank">
+ <i className="icon icon-question-sign"></i>
+ </a>
+ );
+ },
+
render: function () {
+ if (!this.state.visible || !this.state.endpoint) {
+ return null;
+ }
+
return (
<Tray id="api-bar-controller" ref="tray">
@@ -1102,24 +1139,21 @@ function (app, FauxtonAPI, React, FauxtonComponents, ace, beautifyHelper) {
text="API URL" />
<TrayContents
- className="api-bar-tray"
- >
+ className="api-bar-tray">
<div className="input-prepend input-append">
<span className="add-on">
API URL
- <a className="help-link" data-bypass="true" href={this.props.documentation} target="_blank">
- <i className="icon icon-question-sign"></i>
- </a>
+ {this.getDocIcon()}
</span>
<FauxtonComponents.ClipboardWithTextField
onClipBoardClick={this.showCopiedMessage}
text="Copy"
- textToCopy={this.props.endpoint}
+ textToCopy={this.state.endpoint}
uniqueKey="clipboard-apiurl" />
<div className="add-on">
- <a data-bypass="true" href={this.props.endpoint} target="_blank" className="btn">
+ <a data-bypass="true" href={this.state.endpoint} target="_blank" className="btn">
<i className="fonticon-eye icon"></i>
View JSON
</a>
@@ -1129,7 +1163,7 @@ function (app, FauxtonAPI, React, FauxtonComponents, ace, beautifyHelper) {
</TrayContents>
</Tray>
);
- },
+ }
});
return {
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/087e23fd/app/addons/components/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/components/stores.js b/app/addons/components/stores.js
new file mode 100644
index 0000000..d5ec7d5
--- /dev/null
+++ b/app/addons/components/stores.js
@@ -0,0 +1,87 @@
+// 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',
+ 'app',
+ 'addons/components/actiontypes'
+],
+
+function (FauxtonAPI, app, ActionTypes) {
+ var Stores = {};
+
+
+ Stores.ComponentStore = FauxtonAPI.Store.extend({
+ initialize: function () {
+ this.reset();
+ },
+
+ reset: function () {
+ this._apiBarVisible = true;
+ this._endpoint = '';
+ this._docURL = FauxtonAPI.constants.DOC_URLS.GENERAL;
+ },
+
+ isAPIBarVisible: function () {
+ return this._apiBarVisible;
+ },
+
+ updateAPIBar: function (settings) {
+ if (!_.isUndefined(settings.visible)) {
+ this._apiBarVisible = settings.visible;
+ }
+ if (!_.isUndefined(settings.endpoint)) {
+ this._endpoint = settings.endpoint;
+ }
+ if (!_.isUndefined(settings.docURL)) {
+ this._docURL = settings.docURL;
+ }
+ },
+
+ getEndpoint: function () {
+ return this._endpoint;
+ },
+
+ getDocURL: function () {
+ return this._docURL;
+ },
+
+ dispatch: function (action) {
+ switch (action.type) {
+ case ActionTypes.SHOW_API_BAR:
+ this._apiBarVisible = true;
+ this.triggerChange();
+ break;
+
+ case ActionTypes.HIDE_API_BAR:
+ this._apiBarVisible = false;
+ this.triggerChange();
+ break;
+
+ case ActionTypes.UPDATE_API_BAR:
+ this.updateAPIBar(action.options);
+ this.triggerChange();
+ break;
+
+ default:
+ return;
+ // do nothing
+ }
+ }
+ });
+
+ Stores.componentStore = new Stores.ComponentStore();
+ Stores.componentStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.componentStore.dispatch);
+
+ return Stores;
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/087e23fd/app/addons/components/tests/apiBarControllerSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/apiBarControllerSpec.react.jsx b/app/addons/components/tests/apiBarControllerSpec.react.jsx
new file mode 100644
index 0000000..3f516d8
--- /dev/null
+++ b/app/addons/components/tests/apiBarControllerSpec.react.jsx
@@ -0,0 +1,134 @@
+// 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/actions',
+ 'addons/components/stores',
+ 'addons/components/react-components.react',
+ 'testUtils',
+ 'react'
+], function (FauxtonAPI, Actions, Stores, ReactComponents, utils, React) {
+
+ var assert = utils.assert;
+ var TestUtils = React.addons.TestUtils;
+ var componentStore = Stores.componentStore;
+ var ApiBarController = ReactComponents.ApiBarController;
+
+
+ describe('ApiBarController', function () {
+ var container;
+
+ beforeEach(function () {
+ container = document.createElement('div');
+ });
+
+ afterEach(function () {
+ React.unmountComponentAtNode(container);
+ componentStore.reset();
+ });
+
+ it('Doesn\'t show up when explicitly set to visible false', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ Actions.updateAPIBar({
+ visible: false,
+ endpoint: 'http://link.com',
+ docURL: 'http://link.com'
+ });
+ assert.equal(el.getDOMNode(), null);
+ });
+
+ it('Shows up when set to visible', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ Actions.updateAPIBar({
+ visible: true,
+ endpoint: 'http://link.com',
+ docURL: 'http://link.com'
+ });
+ assert.notEqual(el.getDOMNode(), null);
+ });
+
+ it('Doesn\'t show up when set to visible BUT there\'s no endpoint defined', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ Actions.updateAPIBar({
+ visible: true,
+ endpoint: '',
+ docURL: 'http://link.com'
+ });
+ assert.equal(el.getDOMNode(), null);
+ });
+
+ it('Confirm hide/show actions update component', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+
+ // set an initial value
+ Actions.updateAPIBar({ endpoint: 'http://link.com' });
+
+ Actions.showAPIBar();
+ assert.notEqual(el.getDOMNode(), null);
+
+ Actions.hideAPIBar();
+ assert.equal(el.getDOMNode(), null);
+ });
+
+ it('Confirm doc link icon appears when docURL set', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ Actions.updateAPIBar({ visible: true, endpoint: 'http://link.com', docURL: 'http://doc.com' });
+
+ TestUtils.Simulate.click($(el.getDOMNode()).find('.control-toggle-api-url')[0]);
+ assert.equal($(el.getDOMNode()).find('.help-link').length, 1);
+ });
+
+ it('Confirm doc link icon doesn\'t appear with no docURL', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ Actions.updateAPIBar({ visible: true, endpoint: 'http://link.com', docURL: null });
+
+ TestUtils.Simulate.click($(el.getDOMNode()).find('.control-toggle-api-url')[0]);
+ assert.equal($(el.getDOMNode()).find('.help-link').length, 0);
+ });
+
+ it('Confirm endpoint appears in markup', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ var link = 'http://booyah.ca';
+ Actions.updateAPIBar({ visible: true, endpoint: link, docURL: null });
+
+ TestUtils.Simulate.click($(el.getDOMNode()).find('.control-toggle-api-url')[0]);
+ assert.equal($(el.getDOMNode()).find('.text-field-to-copy').val(), link);
+ });
+
+ it('Confirm endpoint is updated in markup', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ var link = 'http://booyah.ca';
+ Actions.updateAPIBar({ visible: true, endpoint: link, docURL: null });
+
+ TestUtils.Simulate.click($(el.getDOMNode()).find('.control-toggle-api-url')[0]);
+ assert.equal($(el.getDOMNode()).find('.text-field-to-copy').val(), link);
+
+ var newLink = 'http://chickensarenoisy.com';
+ Actions.updateAPIBar({ endpoint: newLink });
+ assert.equal($(el.getDOMNode()).find('.text-field-to-copy').val(), newLink);
+ });
+
+ it('Confirm doc URL is updated in markup after a change', function () {
+ var el = TestUtils.renderIntoDocument(<ApiBarController />, container);
+ var docLink = 'http://mydoc.org';
+ Actions.updateAPIBar({ visible: true, endpoint: 'http://whatever.com', docURL: docLink });
+
+ TestUtils.Simulate.click($(el.getDOMNode()).find('.control-toggle-api-url')[0]);
+ assert.equal($(el.getDOMNode()).find('.help-link').attr('href'), docLink);
+
+ var newDocLink = 'http://newawesomedoclink.xxx';
+ Actions.updateAPIBar({ docURL: newDocLink });
+ assert.equal($(el.getDOMNode()).find('.help-link').attr('href'), newDocLink);
+ });
+
+ });
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/087e23fd/app/addons/components/tests/storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/storesSpec.js b/app/addons/components/tests/storesSpec.js
new file mode 100644
index 0000000..f74a8d6
--- /dev/null
+++ b/app/addons/components/tests/storesSpec.js
@@ -0,0 +1,68 @@
+// 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([
+ 'app',
+ 'api',
+ 'testUtils',
+ 'addons/components/stores',
+ 'addons/components/actions'
+], function (app, FauxtonAPI, utils, Stores, ComponentActions, Resources) {
+
+ var assert = utils.assert;
+ var componentStore = Stores.componentStore;
+
+ describe('Components Store', function () {
+
+ afterEach(function () {
+ componentStore.reset();
+ });
+
+ it("UPDATE_API_BAR only updates whatever data is passed", function () {
+ var url = 'http://whoanelly.com';
+ var docURL = 'http://website.com/docs';
+ ComponentActions.updateAPIBar({
+ visible: false,
+ endpoint: url,
+ docURL: docURL
+ });
+ assert.equal(componentStore.isAPIBarVisible(), false);
+ assert.equal(componentStore.getEndpoint(), url);
+ assert.equal(componentStore.getDocURL(), docURL);
+
+ ComponentActions.updateAPIBar({
+ visible: true
+ });
+ assert.equal(componentStore.isAPIBarVisible(), true);
+ assert.equal(componentStore.getEndpoint(), url);
+ assert.equal(componentStore.getDocURL(), docURL);
+
+ var newEndpoint = 'http://movies.com';
+ ComponentActions.updateAPIBar({
+ endpoint: newEndpoint
+ });
+ assert.equal(componentStore.isAPIBarVisible(), true);
+ assert.equal(componentStore.getEndpoint(), newEndpoint);
+ assert.equal(componentStore.getDocURL(), docURL);
+
+ var newDocURL = 'http://newwebsite.org';
+ ComponentActions.updateAPIBar({
+ docURL: newDocURL
+ });
+ assert.equal(componentStore.isAPIBarVisible(), true);
+ assert.equal(componentStore.getEndpoint(), newEndpoint);
+ assert.equal(componentStore.getDocURL(), newDocURL);
+ });
+
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/087e23fd/app/addons/fauxton/base.js
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/base.js b/app/addons/fauxton/base.js
index 85cd44e..9897aae 100644
--- a/app/addons/fauxton/base.js
+++ b/app/addons/fauxton/base.js
@@ -19,11 +19,12 @@ define([
"addons/fauxton/navigation/components.react",
"addons/fauxton/navigation/actions",
'addons/fauxton/dependencies/ZeroClipboard',
- 'addons/components/react-components.react'
+ 'addons/components/react-components.react',
+ 'addons/components/actions'
],
function (app, FauxtonAPI, Components, NotificationComponents, Actions, NavbarReactComponents, NavigationActions,
- ZeroClipboard, ReactComponents) {
+ ZeroClipboard, ReactComponents, ComponentActions) {
var Fauxton = FauxtonAPI.addon();
FauxtonAPI.addNotification = function (options) {
@@ -61,12 +62,18 @@ function (app, FauxtonAPI, Components, NotificationComponents, Actions, NavbarRe
FauxtonAPI.RouteObject.on('beforeFullRender', function (routeObject) {
NavigationActions.setNavbarActiveLink(_.result(routeObject, 'selectedHeader'));
+ // always attempt to render the API Bar. Even if it's hidden on initial load, it may be enabled later
+ routeObject.setComponent('#api-navbar', ReactComponents.ApiBarController);
+
if (routeObject.get('apiUrl')) {
var apiAndDocs = routeObject.get('apiUrl');
- routeObject.setComponent('#api-navbar', ReactComponents.ApiBarController, {
+ ComponentActions.updateAPIBar({
+ visible: true,
endpoint: apiAndDocs[0],
- documentation: apiAndDocs[1]
+ docURL: apiAndDocs[1]
});
+ } else {
+ ComponentActions.hideAPIBar();
}
if (!routeObject.get('hideNotificationCenter')) {