From ca6967452abc33d6d38e08e0745b9797d7d7ae0f Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Wed, 30 Sep 2015 17:03:30 +0200 Subject: [PATCH] SONAR-6834 add search form --- .../js/apps/background-tasks/constants.js | 3 + .../src/main/js/apps/background-tasks/main.js | 20 ++++++- .../main/js/apps/background-tasks/search.js | 59 +++++++++++++++---- .../tests/apps/background-tasks-test.js | 47 ++++++++++++++- 4 files changed, 113 insertions(+), 16 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/background-tasks/constants.js b/server/sonar-web/src/main/js/apps/background-tasks/constants.js index 68c14edb5b0..9988ee344fd 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/constants.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/constants.js @@ -22,3 +22,6 @@ export const DATE = { export const DATE_FORMAT = 'YYYY-MM-DD'; + + +export const DEBOUNCE_DELAY = 250; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/main.js b/server/sonar-web/src/main/js/apps/background-tasks/main.js index ddffd8d8831..b9b7ced5138 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/main.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/main.js @@ -19,7 +19,8 @@ export default React.createClass({ activityPage: 1, statusFilter: STATUSES.ALL, currentsFilter: CURRENTS.ALL, - dateFilter: DATE.ANY + dateFilter: DATE.ANY, + searchQuery: '' }; }, @@ -77,6 +78,9 @@ export default React.createClass({ if (this.state.dateFilter !== DATE.ANY) { _.extend(filters, this.getDateFilter()); } + if (this.state.searchQuery) { + _.extend(filters, { componentQuery: this.state.searchQuery }); + } return filters; }, @@ -158,6 +162,10 @@ export default React.createClass({ }, this.requestData); }, + onSearch(query) { + this.setState({ searchQuery: query }, this.requestData); + }, + loadMore() { this.setState({ activityPage: this.state.activityPage + 1 }, this.requestActivity); }, @@ -186,9 +194,17 @@ export default React.createClass({ return (
+ - + + + + diff --git a/server/sonar-web/src/main/js/apps/background-tasks/search.js b/server/sonar-web/src/main/js/apps/background-tasks/search.js index 994a035a9be..d70ab5e1337 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/search.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/search.js @@ -1,7 +1,8 @@ import $ from 'jquery'; +import _ from 'underscore'; import React from 'react'; import RadioToggle from '../../components/shared/radio-toggle'; -import {STATUSES, CURRENTS, DATE, DATE_FORMAT} from './constants'; +import {STATUSES, CURRENTS, DATE, DATE_FORMAT, DEBOUNCE_DELAY} from './constants'; export default React.createClass({ componentDidUpdate() { @@ -12,6 +13,10 @@ export default React.createClass({ this.attachDatePicker(); }, + componentWillMount() { + this.onSearch = _.debounce(this.onSearch, DEBOUNCE_DELAY); + }, + getCurrentsOptions() { return [ { value: CURRENTS.ALL, label: 'All' }, @@ -36,17 +41,6 @@ export default React.createClass({ ]; }, - attachDatePicker() { - let opts = { - dateFormat: 'yy-mm-dd', - changeMonth: true, - changeYear: true, - onSelect: this.onDateInputChange - }; - $(React.findDOMNode(this.refs.minDate)).datepicker(opts); - $(React.findDOMNode(this.refs.maxDate)).datepicker(opts); - }, - onDateChange(newDate) { if (newDate === DATE.CUSTOM) { let minDateRaw = React.findDOMNode(this.refs.minDate).value, @@ -65,6 +59,19 @@ export default React.createClass({ this.onDateChange(DATE.CUSTOM); }, + attachDatePicker() { + let opts = { + dateFormat: 'yy-mm-dd', + changeMonth: true, + changeYear: true, + onSelect: this.onDateInputChange + }; + if ($.fn.datepicker) { + $(React.findDOMNode(this.refs.minDate)).datepicker(opts); + $(React.findDOMNode(this.refs.maxDate)).datepicker(opts); + } + }, + renderCustomDateInput() { let shouldBeVisible = this.props.dateFilter === DATE.CUSTOM, className = shouldBeVisible ? 'spacer-top' : 'spacer-top hidden'; @@ -78,6 +85,33 @@ export default React.createClass({ ); }, + onSearchFormSubmit(e) { + e.preventDefault(); + this.onSearch(); + }, + + onSearch() { + let searchInput = React.findDOMNode(this.refs.searchInput), + query = searchInput.value; + this.props.onSearch(query); + }, + + renderSearchBox() { + if (this.props.options.componentId) { + // do not render search form on the project-level page + return null; + } + return ( +
+ + +
+ ); + }, + render() { return (
@@ -95,6 +129,7 @@ export default React.createClass({ name="background-task-date" onCheck={this.onDateChange}/> {this.renderCustomDateInput()} +
  • {this.renderSearchBox()}
  • ); diff --git a/server/sonar-web/tests/apps/background-tasks-test.js b/server/sonar-web/tests/apps/background-tasks-test.js index d9588cc26ab..a5501a5ec0a 100644 --- a/server/sonar-web/tests/apps/background-tasks-test.js +++ b/server/sonar-web/tests/apps/background-tasks-test.js @@ -1,10 +1,14 @@ import React from 'react/addons'; import App from '../../src/main/js/apps/background-tasks/app'; import Header from '../../src/main/js/apps/background-tasks/header'; -import {STATUSES, CURRENTS} from '../../src/main/js/apps/background-tasks/constants'; +import Search from '../../src/main/js/apps/background-tasks/search'; +import {STATUSES, CURRENTS, DEBOUNCE_DELAY} from '../../src/main/js/apps/background-tasks/constants'; let TestUtils = React.addons.TestUtils; -let expect = require('chai').expect; +let chai = require('chai'); +let expect = chai.expect; +let sinon = require('sinon'); +chai.use(require('sinon-chai')); describe('Background Tasks', function () { describe('App', () => { @@ -32,4 +36,43 @@ describe('Background Tasks', function () { expect(header.length).to.equal(1); }); }); + + describe('Search', () => { + it('should render search form', () => { + let spy = sinon.spy(); + let component = TestUtils.renderIntoDocument(), + searchBox = TestUtils.scryRenderedDOMComponentsWithClass(component, 'search-box'); + expect(searchBox).to.have.length(1); + }); + + it('should not render search form', () => { + let spy = sinon.spy(); + let component = TestUtils.renderIntoDocument(), + searchBox = TestUtils.scryRenderedDOMComponentsWithClass(component, 'search-box'); + expect(searchBox).to.be.empty; + }); + + it('should search', (done) => { + let spy = sinon.spy(), + searchSpy = sinon.spy(); + let component = TestUtils.renderIntoDocument(); + let searchInput = React.findDOMNode(TestUtils.findRenderedDOMComponentWithClass(component, 'search-box-input')); + searchInput.value = 'some search query'; + TestUtils.Simulate.change(searchInput); + setTimeout(() => { + expect(searchSpy).to.have.been.calledWith('some search query'); + done(); + }, DEBOUNCE_DELAY); + }); + }); }); -- 2.39.5