You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by mi...@apache.org on 2015/06/03 21:22:28 UTC
fauxton commit: updated refs/heads/master to b4e5f53
Repository: couchdb-fauxton
Updated Branches:
refs/heads/master 5d1cb4378 -> b4e5f5353
Add loading lines to active tasks
- Add tabs for filtering
- Add source sequence tray
- garrensmith helped me out a whole bunch with this
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/b4e5f535
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/b4e5f535
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/b4e5f535
Branch: refs/heads/master
Commit: b4e5f535309d6a807d1628d9cbf5eef91d105190
Parents: 5d1cb43
Author: Michelle Phung <mi...@gmail.com>
Authored: Wed Jun 3 09:50:28 2015 +0200
Committer: michellephung@gmail.com <mi...@gmail.com>
Committed: Wed Jun 3 14:09:56 2015 -0400
----------------------------------------------------------------------
app/addons/activetasks/actions.js | 18 +-
app/addons/activetasks/actiontypes.js | 3 +-
.../activetasks/assets/less/activetasks.less | 171 +++++++----
app/addons/activetasks/components.react.jsx | 294 +++++++++----------
app/addons/activetasks/routes.js | 4 +-
app/addons/activetasks/stores.js | 36 ++-
.../tests/activetasks.componentsSpec.react.jsx | 25 +-
.../activetasks/tests/activetasks.storesSpec.js | 45 +--
app/addons/fauxton/components.react.jsx | 35 +++
9 files changed, 376 insertions(+), 255 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/actions.js b/app/addons/activetasks/actions.js
index 74fae94..55469ab 100644
--- a/app/addons/activetasks/actions.js
+++ b/app/addons/activetasks/actions.js
@@ -16,17 +16,17 @@ define([
],
function (FauxtonAPI, ActionTypes, Resources) {
return {
- fetchAndSetActiveTasks: function (options) {
- var activeTasks = options;
-
+ init: function (activeTasks) {
+ this.fetchAndSetActiveTasks(activeTasks.table, activeTasks);
FauxtonAPI.when(activeTasks.fetch()).then(function () {
- this.init(activeTasks.table, activeTasks);
+ this.fetchAndSetActiveTasks(activeTasks.table, activeTasks);
+ this.setActiveTaskIsLoading(false);
}.bind(this));
},
- init: function (collection, backboneCollection) {
+ fetchAndSetActiveTasks: function (collection, backboneCollection) {
FauxtonAPI.dispatch({
- type: ActionTypes.ACTIVE_TASKS_INIT,
+ type: ActionTypes.ACTIVE_TASKS_FETCH_AND_SET,
options: {
collectionTable: collection,
backboneCollection: backboneCollection
@@ -64,6 +64,12 @@ function (FauxtonAPI, ActionTypes, Resources) {
columnName: columnName
}
});
+ },
+ setActiveTaskIsLoading: function (boolean) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.ACTIVE_TASKS_SET_IS_LOADING,
+ options: boolean
+ });
}
};
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/actiontypes.js b/app/addons/activetasks/actiontypes.js
index 8cd2838..cdba87d 100644
--- a/app/addons/activetasks/actiontypes.js
+++ b/app/addons/activetasks/actiontypes.js
@@ -16,6 +16,7 @@ define([], function () {
ACTIVE_TASKS_SET_COLLECTION: 'ACTIVE_TASKS_SET_COLLECTION',
ACTIVE_TASKS_SET_SEARCH_TERM: 'ACTIVE_TASKS_SET_SEARCH_TERM',
ACTIVE_TASKS_SORT_BY_COLUMN_HEADER: 'ACTIVE_TASKS_SORT_BY_COLUMN_HEADER',
- ACTIVE_TASKS_INIT: 'ACTIVE_TASKS_INIT'
+ ACTIVE_TASKS_FETCH_AND_SET: 'ACTIVE_TASKS_FETCH_AND_SET',
+ ACTIVE_TASKS_SET_IS_LOADING: 'ACTIVE_TASKS_SET_IS_LOADING'
};
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/assets/less/activetasks.less
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/assets/less/activetasks.less b/app/addons/activetasks/assets/less/activetasks.less
index 1116671..e11c0a8 100644
--- a/app/addons/activetasks/assets/less/activetasks.less
+++ b/app/addons/activetasks/assets/less/activetasks.less
@@ -10,6 +10,10 @@
// License for the specific language governing permissions and limitations under
// the License.
+@import "../../../../../assets/less/bootstrap/variables.less";
+@import "../../../../../assets/less/variables.less";
+@import "../../../../../assets/less/bootstrap/mixins.less";
+
.task-tabs li,
.active-tasks th {
cursor: pointer;
@@ -65,7 +69,7 @@
}
.no-matching-database-on-search {
- color: #e33f3b;
+ color: #af2d24;
}
p.multiline-active-tasks-message {
@@ -88,67 +92,63 @@ p.multiline-active-tasks-message {
}
}
-.dashboard-lower-menu {
- padding-top: 90px;
- padding-left: 20px;
-}
+#active-tasks-filter-tabs {
-input[type="text"].searchbox {
- width: 200px;
- height: 40px;
- float: right;
- margin:0px;
-}
+ height: 100px;
+ background-color: #CBCBCB;
+ padding-left: 20px;
+ min-width: 550px;
+ overflow: scroll;
-#toggle-filter-tab:hover {
- color: white;
- background-color: #e33f3b;
-}
-.filter-tray.toggleFilterTray-enter { // starting css
- overflow: hidden;
- padding-top: 0px;
- max-height: 0px;
-}
- // animate opening
-.filter-tray.toggleFilterTray-enter.toggleFilterTray-enter-active {
- max-height: 300px;
- height: auto;
- transition: max-height 0.5s linear;
-}
+ input {
+ display:none;
+ }
-.filter-tray { // final css
- height: auto;
- max-height: 300px;
- overflow: hidden;
-}
- // animate closing
-.filter-tray.toggleFilterTray-leave.toggleFilterTray-leave-active {
- max-height: 0;
- padding-top: 0px;
- transition: max-height 0.5s linear;
-}
+ li {
+ background-color: #eee;
+ margin-top: 65px;
+ margin-left: 3px;
+ &.active-tasks-checked {
+ background-color: #af2d24;
+
+ label {
+ color: #fff;
+ }
+ };
+
+ &:hover {
+ border: 1px solid #af2d24;
+ color: #af2d24;
+ };
+
+ label {
+ height: 35px;
+ width: 100%;
+ line-height: 35px;
+ text-align: center;
+ }
+ }
-.filter-checkboxes {
- float: left;
- height: auto;
- width: auto;
- margin-top: 10px;
-}
+ #active-tasks-search-box {
+ display: inline;
+ font-size: 14px;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ height: 36px;
+ background-color: #eee;
+ width: 200px;
+ margin: 0px;
-.active-tasks-one-checkbox {
- input {
- vertical-align: middle;
- margin-right: 5px;
- margin-bottom: 3px;
+ &:focus {
+ background-color: #fff;
+ outline-color: #fff;
+ };
}
- margin-right: 10px;
- display: inline-block;
}
-.active-tasks-checkbox-label {
- display: inline-block;
- color: #666;
+.dashboard-lower-menu {
+ padding-top: 90px;
+ padding-left: 20px;
}
@media (max-width: 940px) {
@@ -159,16 +159,13 @@ input[type="text"].searchbox {
}
}
-.filter-checkboxes-form {
- margin:0px;
-}
-
.polling-interval-widget {
width: 250px;
margin-right: 20px;
margin-top: 10px;
color: #666;
+ float:right;
li {
list-style-type: none;
@@ -208,4 +205,64 @@ input[type="text"].searchbox {
padding: 0px;
margin: 0px;
}
+
+ .active-tasks-header {
+ background-color: #ccc;
+ }
+
+ .icon-caret-up, .icon-caret-down {
+ color: #af2d24;
+ }
+}
+
+.active-tasks-loading-lines {
+ padding-top: 15px;
+ float: left;
+ margin-left: -40px;
+
+ #line1, #line2, #line3, #line4 {
+ background-color: #bbb;
+ }
+}
+
+.view-source-sequence-btn { // "View" Button
+ background-color: #666;
+ display: inline;
+ border-radius: 3px;
+ padding: 5px;
+ color: #fff !important;
+}
+
+.view_source_sequence_tray {
+ padding: 16px 20px 28px;
+
+ position: relative;
+ min-width: 360px;
+ top: 15px;
+ float: right;
+
+ &:before {
+ right: 20px;
+ }
+ input.input-xxlarge {
+ margin-bottom: 0px;
+ width: 250px;
+ .border-radius(5px 0 0 5px);
+ }
+
+ a.btn {
+ color: white;
+ background-color: #af2d24;
+ margin-left: 0;
+ line-height: 1.5em;
+ border: 0px;
+ padding: 10px 10px 9px;
+ font-size: 14px;
+ .border-radius(0 5px 5px 0);
+
+ &:hover, &.copy-button.zeroclipboard-is-hover {
+ background-color: #cbcbcb;
+ color: white;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/components.react.jsx b/app/addons/activetasks/components.react.jsx
index 0110580..5f146ab 100644
--- a/app/addons/activetasks/components.react.jsx
+++ b/app/addons/activetasks/components.react.jsx
@@ -16,8 +16,10 @@ define([
'react',
'addons/activetasks/stores',
'addons/activetasks/resources',
- 'addons/activetasks/actions'
-], function (app, FauxtonAPI, React, Stores, Resources, Actions) {
+ 'addons/activetasks/actions',
+ 'addons/components/react-components.react',
+ 'addons/fauxton/components.react'
+], function (app, FauxtonAPI, React, Stores, Resources, Actions, Components, ComponentsReact) {
var activeTasksStore = Stores.activeTasksStore;
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
@@ -34,7 +36,7 @@ define([
headerIsAscending: activeTasksStore.getHeaderIsAscending(),
setPolling: activeTasksStore.setPolling,
- clearPolling: activeTasksStore.clearPolling,
+ clearPolling: activeTasksStore.clearPolling
};
},
@@ -60,7 +62,7 @@ define([
Actions.setSearchTerm(searchTerm);
},
- switchTab: function (newRadioButton) { //radio buttons
+ switchTab: function (newRadioButton) { //tabs buttons
Actions.switchTab(newRadioButton);
},
@@ -84,14 +86,14 @@ define([
return (
<div className="scrollable">
<div className="inner">
- <ActiveTasksFilter
- searchTerm={searchTerm}
- selectedRadio={selectedRadio}
- onSearch={setSearchTerm}
+ <ActiveTasksFilterTabs
+ searchTerm={searchTerm}
+ selectedRadio={selectedRadio}
+ onSearch={setSearchTerm}
onRadioClick={this.switchTab}/>
- <ActiveTaskTable
- collection={collection}
- searchTerm={searchTerm}
+ <ActiveTaskTable
+ collection={collection}
+ searchTerm={searchTerm}
selectedRadio={selectedRadio}
onTableHeaderClick={onTableHeaderClick}
sortByHeader={sortByHeader}
@@ -102,93 +104,7 @@ define([
}
});
- var ActiveTasksFilter = React.createClass({
- getStoreState: function () {
- return {
- isFilterTrayVisible: false
- };
- },
-
- getInitialState: function () {
- return this.getStoreState();
- },
-
- toggleFilterTray: function () {
- this.setState({
- isFilterTrayVisible : !this.state.isFilterTrayVisible
- });
- },
-
- render: function () {
- var filterTray = '';
-
- if (this.state.isFilterTrayVisible) {
- filterTray = <ActiveTasksFilterTray
- key="filter-tray"
- selectedRadio={this.props.selectedRadio}
- onSearch={this.props.onSearch}
- onRadioClick={this.props.onRadioClick} />;
- }
-
- return (
- <div id="dashboard-upper-content">
- <div className="dashboard-upper-menu active-tasks">
- <ActiveTasksFilterTab onClick={this.toggleFilterTray} />
- </div>
- <ReactCSSTransitionGroup
- className="dashboard-lower-menu"
- transitionName="toggleFilterTray"
- component="div" >
- {filterTray}
- </ReactCSSTransitionGroup>
- </div>
- );
- }
- });
-
- var ActiveTasksFilterTab = React.createClass({
- render: function () {
- return (
- <ul className="nav nav-tabs" id="db-views-tabs-nav">
- <li>
- <a id="toggle-filter-tab"
- className="toggle-filter-tab"
- data-bypass="true"
- data-toggle="button"
- onClick={this.props.onClick}>
- <i className="fonticon fonticon-plus"></i>
- Filter
- </a>
- </li>
- </ul>);
- }
- });
-
- var ActiveTasksFilterTray = React.createClass({
- searchTermChange: function (e) {
- var searchTerm = e.target.value;
- this.props.onSearch(searchTerm);
- },
-
- render: function () {
- return (
- <div className="filter-tray">
- <ActiveTasksFilterTrayCheckBoxes
- onRadioClick={this.props.onRadioClick}
- selectedRadio={this.props.selectedRadio} />
- <input
- className="searchbox"
- type="text"
- name="search"
- placeholder="Search for databases..."
- value={this.props.searchTerm}
- onChange={this.searchTermChange} />
- </div>
- );
- }
- });
-
- var ActiveTasksFilterTrayCheckBoxes = React.createClass({
+ var ActiveTasksFilterTabs = React.createClass({
getDefaultProps: function () {
return {
radioNames : [
@@ -210,21 +126,24 @@ define([
this.props.onRadioClick(radioName);
},
- createCheckboxes: function () {
+ createFilterTabs: function () {
return (
this.props.radioNames.map(function (radioName) {
var checked = this.checked(radioName);
var id = radioName.replace(' ', '-');
var radioClassName = "radio-" + id;
var radioClick = this.onRadioClick;
+ var checkedClassName = checked ? 'active-tasks-checked' : '';
return (
- <li className="active-tasks-one-checkbox" key={radioName + "li"}>
+ <li className={"active-tasks-one-checkbox " + checkedClassName} key={radioName + "li"}>
<input
+ className="toggle-filter-tab"
+ data-bypass="true"
id={id}
type="radio"
- key ={radioName}
- name="radio-button-active-task-filter-tray"
+ key ={radioName}
+ name="radio-button-active-task-filter-tray"
value={radioName}
checked={checked}
onChange={radioClick} />
@@ -237,15 +156,27 @@ define([
);
},
+ searchTermChange: function (e) {
+ var searchTerm = e.target.value;
+ this.props.onSearch(searchTerm);
+ },
+
render: function () {
- var filterCheckboxes = this.createCheckboxes();
+ var filterTabs = this.createFilterTabs();
return (
- <ul className="filter-checkboxes">
- <form className="filter-checkboxes-form">
- {filterCheckboxes}
- </form>
- </ul>
- );
+ <ul className="nav nav-tabs" id="active-tasks-filter-tabs">
+ {filterTabs}
+ <li>
+ <input
+ id="active-tasks-search-box"
+ className="searchbox"
+ type="text"
+ name="search"
+ placeholder="Search for databases..."
+ value={this.props.searchTerm}
+ onChange={this.searchTermChange} />
+ </li>
+ </ul>);
}
});
@@ -261,13 +192,13 @@ define([
return (
<div id="dashboard-lower-content">
<table className="table table-bordered table-striped active-tasks">
- <ActiveTasksTableHeader
+ <ActiveTasksTableHeader
onTableHeaderClick={onTableHeaderClick}
sortByHeader={sortByHeader}
headerIsAscending={headerIsAscending}/>
- <ActiveTasksTableBody
- collection={collection}
- selectedRadio={selectedRadio}
+ <ActiveTasksTableBody
+ collection={collection}
+ selectedRadio={selectedRadio}
searchTerm={searchTerm}/>
</table>
</div>
@@ -293,26 +224,21 @@ define([
var onTableHeaderClick = this.props.onTableHeaderClick;
var sortByHeader = this.props.sortByHeader;
var headerIsAscending = this.props.headerIsAscending;
- return (
- this.props.headerNames.map(function (header) {
- return (
- <TableHeader
- headerName={header[0]}
- displayName={header[1]}
- key={header[0]}
- onTableHeaderClick={onTableHeaderClick}
- sortByHeader={sortByHeader}
- headerIsAscending={headerIsAscending} />
- );
- })
- );
+ return this.props.headerNames.map(function (header) {
+ return <TableHeader
+ headerName={header[0]}
+ displayName={header[1]}
+ key={header[0]}
+ onTableHeaderClick={onTableHeaderClick}
+ sortByHeader={sortByHeader}
+ headerIsAscending={headerIsAscending} />;
+ });
},
render: function () {
- var tableHeadingFields = this.createTableHeadingFields();
return (
<thead>
- <tr>{tableHeadingFields}</tr>
+ <tr>{this.createTableHeadingFields()}</tr>
</thead>
);
}
@@ -348,8 +274,8 @@ define([
className="header-field radio"
onChange={this.onTableHeaderClick}>
<th className={th_class} value={this.props.headerName}>
- <label
- className="header-field label-text"
+ <label
+ className="header-field label-text active-tasks-header"
htmlFor={this.props.headerName}>
{this.props.displayName} {arrow}
</label>
@@ -384,8 +310,8 @@ define([
return isThereASearchTerm ? this.noActiveTasks() : this.noActiveTasksMatchFilter();
}
- return _.map(this.state.filteredTable, function (item, iteration) {
- return <ActiveTaskTableBodyContents key={Math.random()} item={item} />;
+ return _.map(this.state.filteredTable, function (item, key) {
+ return <ActiveTaskTableBodyContents key={key} item={item} />;
});
},
@@ -406,10 +332,9 @@ define([
},
render: function () {
- var tableBody = this.createRows();
return (
<tbody className="js-tasks-go-here">
- {tableBody}
+ {this.createRows()}
</tbody>
);
}
@@ -453,17 +378,66 @@ define([
<td>{startedOnMsg}</td>
<td>{updatedOnMsg}</td>
<td>{rowData.pid}</td>
- <td>{progressMsg}</td>
+ <td>{progressMsg}<ActiveTasksViewSourceSequence item={this.props.item}/></td>
</tr>
);
}
});
+ var ActiveTasksViewSourceSequence = React.createClass({
+ onTrayToggle: function (e) {
+ e.preventDefault();
+ this.refs.view_source_sequence_btn.toggle(function (shown) {
+ if (shown) {
+ this.refs.view_source_sequence_btn.getDOMNode().focus();
+ }
+ }.bind(this));
+ },
+
+ sequences: function (item) {
+ if (_.isNumber(item) || _.isString(item)) {
+ return <ComponentsReact.ClipboardWithTextField textToCopy={item} uniqueKey={item}/>;
+ }
+
+ if (_.isArray(item)) {
+ return _.map(item, function (seq, i) {
+ return <ComponentsReact.ClipboardWithTextField textToCopy={seq} uniqueKey={i + Math.random(100)} key={i}/>;
+ });
+ }
+
+ return <ComponentsReact.ClipboardWithTextField textToCopy="???" uniqueKey='unknownRevision'/>;
+ },
+
+ render: function () {
+
+ if (_.has(this.props.item, 'source_seq')) {
+ var sequences = this.sequences(this.props.item.source_seq);
+ return (
+ <div>
+ Current source sequence:
+ <a href="#"
+ className="view-source-sequence-btn"
+ onClick={this.onTrayToggle}
+ data-bypass="true">
+ View
+ </a>
+ <ComponentsReact.Tray ref="view_source_sequence_btn" className="view_source_sequence_tray">
+ <span className="add-on">Source Sequence</span>
+ {sequences}
+ </ComponentsReact.Tray>
+ </div>
+ );
+ }
+ return null;
+ }
+ });
+
var ActiveTasksPollingWidgetController = React.createClass({
getStoreState: function () {
return {
- pollingInterval: activeTasksStore.getPollingInterval()
+ pollingInterval: activeTasksStore.getPollingInterval(),
+ isLoading: false//activeTasksStore.isLoading()
};
},
@@ -497,18 +471,18 @@ define([
return (
<ul className="polling-interval-widget">
<li className="polling-interval-name">Polling interval
- <label className="polling-interval-time-label" htmlFor="pollingRange">
- <span>{pollingInterval}</span> second{s}
+ <label className="polling-interval-time-label" htmlFor="pollingRange">
+ <span>{pollingInterval}</span> second{s}
</label>
</li>
<li>
- <input
- id="pollingRange"
- type="range"
- min="1"
- max="30"
- step="1"
- value={pollingInterval}
+ <input
+ id="pollingRange"
+ type="range"
+ min="1"
+ max="30"
+ step="1"
+ value={pollingInterval}
onChange={onChangeHandle}/>
</li>
</ul>
@@ -517,8 +491,22 @@ define([
render: function () {
var pollingWidget = this.createPollingWidget();
+ var loadLines = null;
+
+ if (this.state.isLoading || this.state.pollingInterval === "1") {
+ // show loading lines constantly if the polling interval is
+ // 1 second, so that the lines aren't choppy
+ loadLines = <Components.LoadLines />;
+ }
- return <div>{pollingWidget}</div>;
+ return (
+ <div>
+ <span className="active-tasks-loading-lines">
+ {loadLines}
+ </span>
+ {pollingWidget}
+ </div>
+ );
}
});
@@ -568,29 +556,29 @@ define([
}
}
- if (_.has(item, 'source_seq')) {
- progressMessage.push('Current source sequence: ' + item.source_seq + '. ');
- }
-
if (_.has(item, 'changes_done')) {
progressMessage.push(item.changes_done + ' Changes done.');
}
return progressMessage;
+ },
+
+ getSourceSequence: function (item) {
+ return item.source_seq;
}
+
};
return {
ActiveTasksController: ActiveTasksController,
- ActiveTasksFilter: ActiveTasksFilter,
- ActiveTasksFilterTab: ActiveTasksFilterTab,
- ActiveTasksFilterTray: ActiveTasksFilterTray,
+ ActiveTasksFilterTabs: ActiveTasksFilterTabs,
ActiveTaskTable: ActiveTaskTable,
ActiveTasksTableHeader: ActiveTasksTableHeader,
TableHeader: TableHeader,
ActiveTasksTableBody: ActiveTasksTableBody,
ActiveTaskTableBodyContents: ActiveTaskTableBodyContents,
+ ActiveTasksViewSourceSequence: ActiveTasksViewSourceSequence,
ActiveTasksPollingWidgetController: ActiveTasksPollingWidgetController
};
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/routes.js
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/routes.js b/app/addons/activetasks/routes.js
index cb9f673..b03d54a 100644
--- a/app/addons/activetasks/routes.js
+++ b/app/addons/activetasks/routes.js
@@ -40,9 +40,7 @@ function (app, FauxtonAPI, ActiveTasksResources, ActiveTasksComponents, Actions)
this.allTasks = new ActiveTasksResources.AllTasks();
},
showActiveTasks: function () {
- Actions.fetchAndSetActiveTasks(this.allTasks);
- Actions.changePollingInterval(1);
-
+ Actions.init(this.allTasks);
this.setComponent('#dashboard-content', ActiveTasksComponents.ActiveTasksController);
this.setComponent('#right-header', ActiveTasksComponents.ActiveTasksPollingWidgetController);
}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/stores.js b/app/addons/activetasks/stores.js
index 4c61a52..baf5607 100644
--- a/app/addons/activetasks/stores.js
+++ b/app/addons/activetasks/stores.js
@@ -18,7 +18,7 @@ define([
var ActiveTasksStore = FauxtonAPI.Store.extend({
- init: function (collectionTable, backboneCollection) {
+ initAfterFetching: function (collectionTable, backboneCollection) {
this._prevSortbyHeader = 'started_on';
this._headerIsAscending = true;
this._selectedRadio = 'All Tasks';
@@ -28,6 +28,29 @@ define([
this._pollingIntervalSeconds = 5;
this.sortCollectionByColumnHeader(this._sortByHeader);
this._backboneCollection = backboneCollection;
+ this.setIsLoading(true, new Date());
+ },
+
+ isLoading: function () {
+ return this._isLoading;
+ },
+
+ setIsLoading: function (bool, time) {
+ if (bool) {
+ this._startTimeForLoading = time;
+ this._isLoading = true;
+ } else {
+ var stoptime = time;
+ var responseTime = stoptime - this._startTimeForLoading;
+ if (responseTime < 800) {
+ setTimeout(function () {
+ this._isLoading = false;
+ this.triggerChange();
+ }.bind(this), 800); //stop after 800ms for smooth animation
+ } else {
+ this._isLoading = false;
+ }
+ }
},
getSelectedRadio: function () {
@@ -47,7 +70,7 @@ define([
},
setPolling: function () {
- clearInterval(this.getIntervalID());
+ this.clearPolling();
var id = setInterval(function () {
this._backboneCollection.pollingFetch();
this.setCollection(this._backboneCollection.table);
@@ -170,8 +193,8 @@ define([
dispatch: function (action) {
switch (action.type) {
- case ActionTypes.ACTIVE_TASKS_INIT:
- this.init(action.options.collectionTable, action.options.backboneCollection);
+ case ActionTypes.ACTIVE_TASKS_FETCH_AND_SET:
+ this.initAfterFetching(action.options.collectionTable, action.options.backboneCollection);
break;
case ActionTypes.ACTIVE_TASKS_CHANGE_POLLING_INTERVAL:
@@ -202,6 +225,11 @@ define([
this.triggerChange();
break;
+ case ActionTypes.ACTIVE_TASKS_SET_IS_LOADING:
+ this.setIsLoading(action.options, new Date());
+ this.triggerChange();
+ break;
+
default:
return;
}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/tests/activetasks.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/tests/activetasks.componentsSpec.react.jsx b/app/addons/activetasks/tests/activetasks.componentsSpec.react.jsx
index 4c22c3d..a147abd 100644
--- a/app/addons/activetasks/tests/activetasks.componentsSpec.react.jsx
+++ b/app/addons/activetasks/tests/activetasks.componentsSpec.react.jsx
@@ -18,8 +18,9 @@ define([
'react',
'addons/activetasks/actions',
'testUtils'
-], function (FauxtonAPI, ActiveTasks, Components, Stores, fakedResponse, React, Actions, testUtils) {
- var assert = testUtils.assert;
+], function (FauxtonAPI, ActiveTasks, Components, Stores, fakedResponse, React, Actions, utils) {
+ var assert = utils.assert;
+ var restore = utils.restore;
var TestUtils = React.addons.TestUtils;
var activeTasksStore = Stores.activeTasksStore;
var activeTasksCollection = new ActiveTasks.AllTasks({});
@@ -33,12 +34,13 @@ define([
beforeEach(function () {
pollingWidgetDiv = document.createElement('div');
pollingWidget = TestUtils.renderIntoDocument(
- React.createElement(Components.ActiveTasksPollingWidgetController, null), pollingWidgetDiv
+ <Components.ActiveTasksPollingWidgetController />, pollingWidgetDiv
);
});
afterEach(function () {
React.unmountComponentAtNode(pollingWidgetDiv);
+ restore(Actions.changePollingInterval);
});
it('should trigger update polling interval', function () {
@@ -56,17 +58,17 @@ define([
beforeEach(function () {
tableDiv = document.createElement('div');
- activeTasksStore.init(activeTasksCollection.table, activeTasksCollection);
- table = TestUtils.renderIntoDocument(React.createElement(Components.ActiveTasksController, null), tableDiv);
+ activeTasksStore.initAfterFetching(activeTasksCollection.table, activeTasksCollection);
+ table = TestUtils.renderIntoDocument(<Components.ActiveTasksController />, tableDiv);
// open filter tray
- filterTab = TestUtils.findRenderedDOMComponentWithClass(table, 'toggle-filter-tab');
- TestUtils.Simulate.click(filterTab);
+ //filterTab = TestUtils.findRenderedDOMComponentWithClass(table, 'toggle-filter-tab');
+ //TestUtils.Simulate.click(filterTab);
});
afterEach(function () {
React.unmountComponentAtNode(tableDiv);
- window.confirm.restore && window.confirm.restore();
+ restore(window.confirm);
});
it('it displays a message instead of an empty table, if there are undefined active tasks', function () {
@@ -78,7 +80,8 @@ define([
describe('Active Tasks Filter tray', function () {
afterEach(function () {
- spy.restore();
+ restore(Actions.switchTab);
+ restore(Actions.setSearchTerm);
});
var radioIDs = [
@@ -114,6 +117,10 @@ define([
'progress'
];
+ afterEach(function () {
+ restore(Actions.sortByColumnHeader);
+ });
+
it('should trigger change to which header to sort by', function () {
_.each(headerNames, function (header) {
spy = sinon.spy(Actions, 'sortByColumnHeader');
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/activetasks/tests/activetasks.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/activetasks/tests/activetasks.storesSpec.js b/app/addons/activetasks/tests/activetasks.storesSpec.js
index 462d94d..c059280 100644
--- a/app/addons/activetasks/tests/activetasks.storesSpec.js
+++ b/app/addons/activetasks/tests/activetasks.storesSpec.js
@@ -12,13 +12,13 @@
define([
'api',
'addons/activetasks/resources',
- 'addons/activetasks/components.react',
'addons/activetasks/stores',
'addons/activetasks/tests/fakeActiveTaskResponse',
'react',
'testUtils'
-], function (FauxtonAPI, ActiveTasks, Components, Stores, fakedResponse, React, testUtils) {
- var assert = testUtils.assert;
+], function (FauxtonAPI, ActiveTasks, Stores, fakedResponse, React, utils) {
+ var assert = utils.assert;
+ var restore = utils.restore;
var TestUtils = React.addons.TestUtils;
var activeTasksStore = Stores.activeTasksStore;
@@ -26,29 +26,30 @@ define([
activeTasksCollection.parse(fakedResponse);
describe('Active Tasks -- Stores', function () {
- var spy;
+ var spy, clock;
beforeEach(function () {
- activeTasksStore.init(activeTasksCollection.table, activeTasksCollection);
- this.clock = sinon.useFakeTimers();
+ activeTasksStore.initAfterFetching(activeTasksCollection.table, activeTasksCollection);
+ clock = sinon.useFakeTimers();
});
afterEach(function () {
- testUtils.restore(spy);
- testUtils.restore(this.clock);
+ restore(spy);
+ restore(clock);
});
describe('Active Task Stores - Polling', function () {
var pollingWidgetDiv, pollingWidget;
beforeEach(function () {
- activeTasksStore.init(activeTasksCollection.table, activeTasksCollection);
- this.clock = sinon.useFakeTimers();
+ activeTasksStore.initAfterFetching(activeTasksCollection.table, activeTasksCollection);
+ clock = sinon.useFakeTimers();
});
afterEach(function () {
- testUtils.restore(spy);
- testUtils.restore(this.clock);
+ restore(activeTasksStore.getPollingInterval);
+ restore(window.clearInterval);
+ restore(clock);
});
it('should poll at the min time', function () {
@@ -59,10 +60,10 @@ define([
assert.ok(spy.calledOnce);
setInterval(spy, minTime * 1000);
- this.clock.tick(minTime * 1000);
+ clock.tick(minTime * 1000);
assert.ok(spy.calledTwice);
- this.clock.tick(minTime * 1000);
+ clock.tick(minTime * 1000);
assert.ok(spy.calledThrice);
});
@@ -75,10 +76,10 @@ define([
assert.ok(spy.calledOnce);
setInterval(spy, maxTime * 1000);
- this.clock.tick(maxTime * 1000);
+ clock.tick(maxTime * 1000);
assert.ok(spy.calledTwice);
- this.clock.tick(maxTime * 1000);
+ clock.tick(maxTime * 1000);
assert.ok(spy.calledThrice);
});
@@ -91,10 +92,10 @@ define([
assert.ok(spy.calledOnce);
setInterval(spy, midtime * 1000);
- this.clock.tick(midtime * 1000);
+ clock.tick(midtime * 1000);
assert.ok(spy.calledTwice);
- this.clock.tick(midtime * 1000);
+ clock.tick(midtime * 1000);
assert.ok(spy.calledThrice);
});
@@ -132,7 +133,7 @@ define([
//parse table and check that it only contains objects with type: Replication
_.each(storeFilteredtable, function (activeTask) {
assert.ok(activeTasksStore.passesRadioFilter(activeTask));
- assert.ok(activeTask.type === activeTasksStore.getSelectedRadio());
+ assert.deepEqual(activeTask.type, activeTasksStore.getSelectedRadio());
});
});
@@ -158,20 +159,20 @@ define([
it('should set header as ascending, on default', function () {
activeTasksStore.setSelectedRadio('all_tasks');
activeTasksStore._headerIsAscending = true;
- assert.ok(activeTasksStore.getHeaderIsAscending() === true);
+ assert.ok(activeTasksStore.getHeaderIsAscending());
});
it('should set header as descending, if same header is selected again', function () {
activeTasksStore._prevSortbyHeader = 'sameHeader';
activeTasksStore._sortByHeader = 'sameHeader';
activeTasksStore.toggleHeaderIsAscending();
- assert.ok(activeTasksStore.getHeaderIsAscending() === false);
+ assert.notOk(activeTasksStore.getHeaderIsAscending());
});
it('should set header as ascending, if different header is selected', function () {
activeTasksStore._sortByHeader = 'differentHeader';
activeTasksStore.toggleHeaderIsAscending();
- assert.ok(activeTasksStore.getHeaderIsAscending() === true);
+ assert.ok(activeTasksStore.getHeaderIsAscending());
});
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b4e5f535/app/addons/fauxton/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/fauxton/components.react.jsx b/app/addons/fauxton/components.react.jsx
index 5e7e891..a08f8a6 100644
--- a/app/addons/fauxton/components.react.jsx
+++ b/app/addons/fauxton/components.react.jsx
@@ -46,6 +46,40 @@ function (app, FauxtonAPI, React, ZeroClipboard) {
}
});
+ // use like this:
+ // <ComponentsReact.ClipboardWithTextField textToCopy={yourText} uniqueKey={someUniqueValue}>
+ // </ComponentsReact.ClipboardWithTextField>
+ // pass in the text and a unique key, the key has to be unique or you'll get a warning
+ var ClipboardWithTextField = React.createClass({
+ componentWillMount: function () {
+ ZeroClipboard.config({ moviePath: app.zeroClipboardPath });
+ },
+
+ componentDidUpdate: function () {
+ this.clipboard = new ZeroClipboard(document.getElementById("copy-text-" + this.props.uniqueKey));
+ },
+
+ render: function () {
+ return (
+ <p key={this.props.uniqueKey}>
+ <input
+ type="text"
+ className="input-xxlarge text-field-to-copy"
+ readOnly
+ value={this.props.textToCopy} />
+ <a
+ id={"copy-text-" + this.props.uniqueKey}
+ className="fonticon-clipboard icon btn copy-button"
+ data-clipboard-text={this.props.textToCopy}
+ data-bypass="true"
+ title="Copy to clipboard" >
+ Copy
+ </a>
+ </p>
+ );
+ }
+ });
+
// formats a block of code and pretty-prints it in the page. Currently uses the prettyPrint plugin
var CodeFormat = React.createClass({
getDefaultProps: function () {
@@ -223,6 +257,7 @@ function (app, FauxtonAPI, React, ZeroClipboard) {
return {
Clipboard: Clipboard,
+ ClipboardWithTextField: ClipboardWithTextField,
CodeFormat: CodeFormat,
Tray: Tray,
Pagination: Pagination