diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-07-11 08:07:28 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-07-13 14:34:17 +0200 |
commit | 8746fbda0290c691705208caad6a7c2590191673 (patch) | |
tree | f5dcf2fcf5affb17e572928a87bec1d1b0f0eac5 /server | |
parent | f0da51dc6ad711b31f92af91050357d41187eb5b (diff) | |
download | sonarqube-8746fbda0290c691705208caad6a7c2590191673.tar.gz sonarqube-8746fbda0290c691705208caad6a7c2590191673.zip |
SONAR-9410 Add date selectors to the project activity page
Diffstat (limited to 'server')
10 files changed, 194 insertions, 18 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js index d982bc837cc..59c9485d937 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js @@ -100,7 +100,12 @@ export default class ProjectActivityApp extends React.PureComponent { <div id="project-activity" className="page page-limited"> <Helmet title={translate('project_activity.page')} /> - <ProjectActivityPageHeader category={query.category} updateQuery={this.props.updateQuery} /> + <ProjectActivityPageHeader + category={query.category} + from={query.from} + to={query.to} + updateQuery={this.props.updateQuery} + /> <div className="layout-page project-activity-page"> <div className="layout-page-side-outer project-activity-page-side-outer boxed-group"> diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js new file mode 100644 index 00000000000..82aa95a96c2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityDateInput.js @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// @flow +import React from 'react'; +import moment from 'moment'; +import DateInput from '../../../components/controls/DateInput'; +import { parseAsDate } from '../../../helpers/query'; +import { translate } from '../../../helpers/l10n'; +import type { RawQuery } from '../../../helpers/query'; + +type Props = { + from: ?Date, + to: ?Date, + onChange: RawQuery => void +}; + +export default class ProjectActivityDateInput extends React.PureComponent { + props: Props; + + handleFromDateChange = (from: string) => this.props.onChange({ from: parseAsDate(from) }); + + handleToDateChange = (to: string) => this.props.onChange({ to: parseAsDate(to) }); + + handleResetClick = () => this.props.onChange({ from: null, to: null }); + + formatDate = (date: ?Date) => (date ? moment(date).format('YYYY-MM-DD') : null); + + render() { + return ( + <div> + <DateInput + className="little-spacer-right" + name="from" + value={this.formatDate(this.props.from)} + placeholder={translate('from')} + onChange={this.handleFromDateChange} + /> + {'—'} + <DateInput + className="little-spacer-left" + name="to" + value={this.formatDate(this.props.to)} + placeholder={translate('to')} + onChange={this.handleToDateChange} + /> + <button + className="spacer-left" + onClick={this.handleResetClick} + disabled={this.props.from == null && this.props.to == null}> + {translate('reset_verb')} + </button> + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js index d6b96f42f42..8f48ceaf91e 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js @@ -69,26 +69,42 @@ export default class ProjectActivityGraphs extends React.PureComponent { } componentWillReceiveProps(nextProps: Props) { + let newSeries; if ( nextProps.measuresHistory !== this.props.measuresHistory || historyQueryChanged(this.props.query, nextProps.query) ) { - const series = generateSeries( + newSeries = generateSeries( nextProps.measuresHistory, nextProps.query.graph, nextProps.metricsType, getDisplayedHistoryMetrics(nextProps.query.graph, nextProps.query.customMetrics) ); - const newDates = this.getStateZoomDates(this.props, nextProps, series); + } + + const newDates = this.getStateZoomDates( + this.props, + nextProps, + newSeries ? newSeries : this.state.series + ); + + if (newSeries || newDates) { + let newState = {}; + if (newSeries) { + newState.series = newSeries; + } if (newDates) { - this.setState({ series, ...newDates }); - } else { - this.setState({ series }); + newState = { ...newState, ...newDates }; } + this.setState(newState); } } - getStateZoomDates = (props: ?Props, nextProps: Props, series: Array<Serie>) => { + getStateZoomDates = ( + props: ?Props, + nextProps: Props, + series: Array<Serie> + ): ?{ graphEndDate: ?Date, graphStartDate: ?Date } => { const newDates = { from: nextProps.query.from || null, to: nextProps.query.to || null }; if (props && datesQueryChanged(props.query, nextProps.query)) { return { graphEndDate: newDates.to, graphStartDate: newDates.from }; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js index e2f8a8569fa..ebbce83df7c 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js @@ -22,13 +22,16 @@ import React from 'react'; import Select from 'react-select'; import ProjectActivityEventSelectOption from './ProjectActivityEventSelectOption'; import ProjectActivityEventSelectValue from './ProjectActivityEventSelectValue'; +import ProjectActivityDateInput from './ProjectActivityDateInput'; import { EVENT_TYPES } from '../utils'; import { translate } from '../../../helpers/l10n'; import type { RawQuery } from '../../../helpers/query'; type Props = { - updateQuery: RawQuery => void, - category?: string + category?: string, + from: ?Date, + to: ?Date, + updateQuery: RawQuery => void }; export default class ProjectActivityPageHeader extends React.PureComponent { @@ -43,15 +46,14 @@ export default class ProjectActivityPageHeader extends React.PureComponent { })); } - handleCategoryChange = (option: ?{ value: string }) => { + handleCategoryChange = (option: ?{ value: string }) => this.props.updateQuery({ category: option ? option.value : '' }); - }; render() { return ( <header className="page-header"> <Select - className="input-medium" + className="input-medium pull-left spacer-right" placeholder={translate('project_activity.filter_events') + '...'} clearable={true} searchable={false} @@ -61,6 +63,12 @@ export default class ProjectActivityPageHeader extends React.PureComponent { options={this.options} onChange={this.handleCategoryChange} /> + <ProjectActivityDateInput + className="pull-left" + from={this.props.from} + to={this.props.to} + onChange={this.props.updateQuery} + /> </header> ); } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.js b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.js new file mode 100644 index 00000000000..fa199664561 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.js @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { shallow } from 'enzyme'; +import ProjectActivityDateInput from '../ProjectActivityDateInput'; + +it('should render correctly the date inputs', () => { + expect( + shallow( + <ProjectActivityDateInput + from={new Date('2016-10-27T12:21:15+0000')} + to={new Date('2016-12-27T12:21:15+0000')} + onChange={() => {}} + /> + ) + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityPageHeader-test.js b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityPageHeader-test.js index e0925df3ff0..9fe17ba339a 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityPageHeader-test.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityPageHeader-test.js @@ -23,6 +23,12 @@ import ProjectActivityPageHeader from '../ProjectActivityPageHeader'; it('should render correctly the list of series', () => { expect( - shallow(<ProjectActivityPageHeader category="" updateQuery={() => {}} />) + shallow( + <ProjectActivityPageHeader + category="" + from={new Date('2016-10-27T12:21:15+0200')} + updateQuery={() => {}} + /> + ) ).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap new file mode 100644 index 00000000000..438df209b7f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityDateInput-test.js.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly the date inputs 1`] = ` +<div> + <DateInput + className="little-spacer-right" + format="yy-mm-dd" + name="from" + onChange={[Function]} + placeholder="from" + value="2016-10-27" + /> + — + <DateInput + className="little-spacer-left" + format="yy-mm-dd" + name="to" + onChange={[Function]} + placeholder="to" + value="2016-12-27" + /> + <button + className="spacer-left" + disabled={false} + onClick={[Function]} + > + reset_verb + </button> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityPageHeader-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityPageHeader-test.js.snap index 19fc3e8375b..a185ef12c0c 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityPageHeader-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityPageHeader-test.js.snap @@ -10,7 +10,7 @@ exports[`should render correctly the list of series 1`] = ` autosize={true} backspaceRemoves={true} backspaceToRemoveMessage="Press backspace to remove {label}" - className="input-medium" + className="input-medium pull-left spacer-right" clearAllText="Clear all" clearValueText="Clear value" clearable={true} @@ -66,5 +66,10 @@ exports[`should render correctly the list of series 1`] = ` valueComponent={[Function]} valueKey="value" /> + <ProjectActivityDateInput + className="pull-left" + from={2016-10-27T10:21:15.000Z} + onChange={[Function]} + /> </header> `; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css index 740aef24ba3..5833f26d7ce 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css @@ -239,7 +239,7 @@ left: 12px; right: 16px; padding-top: 24px; - z-index: 100; + z-index: 1; } .project-activity-version-badge.sticky + .project-activity-days-list { diff --git a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js index 2f657229c61..692a2406ba2 100644 --- a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js +++ b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.js @@ -131,10 +131,10 @@ export default class AdvancedTimeline extends React.PureComponent { } }; - getXScale = (props: Props, availableWidth: number, flatData: Array<Point>) => { + getXScale = ({ startDate, endDate }: Props, availableWidth: number, flatData: Array<Point>) => { const dateRange = extent(flatData, d => d.x); - const start = props.startDate ? props.startDate : dateRange[0]; - const end = props.endDate ? props.endDate : dateRange[1]; + const start = startDate && startDate > dateRange[0] ? startDate : dateRange[0]; + const end = endDate && endDate < dateRange[1] ? endDate : dateRange[1]; const xScale = scaleTime().domain(sortBy([start, end])).range([0, availableWidth]).clamp(false); return { xScale, |