From 3079907d1f13a509709c72a5dade87d129a4eb5c Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Fri, 30 Aug 2019 17:31:15 +0200 Subject: SONAR-12430 Handle project baseline for Community Edition --- server/sonar-web/src/main/js/api/newCodePeriod.ts | 2 +- server/sonar-web/src/main/js/app/types.d.ts | 12 ++- .../projectActivity/components/GraphsHistory.tsx | 3 +- .../components/ProjectActivityAnalysesList.tsx | 7 +- .../components/ProjectActivityAnalysis.tsx | 3 +- .../components/ProjectActivityApp.tsx | 4 +- .../components/ProjectActivityAppContainer.tsx | 9 +- .../components/ProjectActivityGraphs.tsx | 3 +- .../components/forms/AddEventForm.tsx | 3 +- .../components/forms/RemoveAnalysisForm.tsx | 3 +- .../src/main/js/apps/projectActivity/utils.ts | 6 +- .../js/apps/projectBaseline/__tests__/App-test.tsx | 8 +- .../__tests__/BranchBaselineSettingModal-test.tsx | 20 ----- .../__tests__/ProjectBaselineSelector-test.tsx | 11 ++- .../BranchBaselineSettingModal-test.tsx.snap | 2 +- .../ProjectBaselineSelector-test.tsx.snap | 99 +++++++++++++++++++--- .../apps/projectBaseline/__tests__/utils-test.ts | 40 +++++++++ .../js/apps/projectBaseline/components/App.tsx | 81 ++++++++++++------ .../projectBaseline/components/AppContainer.ts | 1 + .../components/BranchAnalysisList.tsx | 8 +- .../components/BranchBaselineSettingModal.tsx | 42 +++------ .../components/ProjectBaselineSelector.tsx | 88 +++++++++++++------ .../src/main/js/apps/projectBaseline/styles.css | 16 ++-- .../src/main/js/apps/projectBaseline/utils.ts | 41 +++++++++ server/sonar-web/src/main/js/helpers/testMocks.ts | 3 +- 25 files changed, 354 insertions(+), 161 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts (limited to 'server/sonar-web/src/main/js') diff --git a/server/sonar-web/src/main/js/api/newCodePeriod.ts b/server/sonar-web/src/main/js/api/newCodePeriod.ts index 1c21cefa735..da2b9d83ea7 100644 --- a/server/sonar-web/src/main/js/api/newCodePeriod.ts +++ b/server/sonar-web/src/main/js/api/newCodePeriod.ts @@ -23,7 +23,7 @@ import throwGlobalError from '../app/utils/throwGlobalError'; export function getNewCodePeriod(data?: { project?: string; branch?: string; -}): Promise<{ type: T.NewCodePeriodSettingType; inherited?: boolean; value?: string }> { +}): Promise> { return getJSON('/api/new_code_periods/show', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/app/types.d.ts b/server/sonar-web/src/main/js/app/types.d.ts index d2a89083b22..703d7b8db5b 100644 --- a/server/sonar-web/src/main/js/app/types.d.ts +++ b/server/sonar-web/src/main/js/app/types.d.ts @@ -52,15 +52,22 @@ declare namespace T { name: string; } - export interface Analysis { + interface BaseAnalysis { buildString?: string; - date: string; events: AnalysisEvent[]; key: string; manualNewCodePeriodBaseline?: boolean; projectVersion?: string; } + export interface Analysis extends BaseAnalysis { + date: string; + } + + export interface ParsedAnalysis extends BaseAnalysis { + date: Date; + } + export interface AnalysisEvent { category: string; description?: string; @@ -507,6 +514,7 @@ declare namespace T { type?: NewCodePeriodSettingType; value?: string; effectiveValue?: string; + inherited?: boolean; } export interface NewCodePeriodBranch { diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx index c52f9217dce..468b80897af 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/GraphsHistory.tsx @@ -26,13 +26,12 @@ import { hasHistoryData, isCustomGraph, MeasureHistory, - ParsedAnalysis, Serie } from '../utils'; import GraphHistory from './GraphHistory'; interface Props { - analyses: ParsedAnalysis[]; + analyses: T.ParsedAnalysis[]; eventFilter: string; graph: string; graphs: Serie[][]; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx index d6de9e60612..25171679c5c 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx @@ -28,7 +28,6 @@ import DateFormatter from '../../../components/intl/DateFormatter'; import { activityQueryChanged, getAnalysesByVersionByDay, - ParsedAnalysis, Query, selectedDateQueryChanged } from '../utils'; @@ -37,7 +36,7 @@ import ProjectActivityAnalysis from './ProjectActivityAnalysis'; interface Props { addCustomEvent: (analysis: string, name: string, category?: string) => Promise; addVersion: (analysis: string, version: string) => Promise; - analyses: ParsedAnalysis[]; + analyses: T.ParsedAnalysis[]; analysesLoading: boolean; canAdmin?: boolean; canDeleteAnalyses?: boolean; @@ -157,14 +156,14 @@ export default class ProjectActivityAnalysesList extends React.PureComponent Promise; addVersion: (analysis: string, version: string) => Promise; - analysis: ParsedAnalysis; + analysis: T.ParsedAnalysis; canAdmin?: boolean; canDeleteAnalyses?: boolean; canCreateVersion: boolean; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx index 8766d537f91..fef42d2314d 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx @@ -23,7 +23,7 @@ import { parseDate } from 'sonar-ui-common/helpers/dates'; import { translate } from 'sonar-ui-common/helpers/l10n'; import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; -import { MeasureHistory, ParsedAnalysis, Query } from '../utils'; +import { MeasureHistory, Query } from '../utils'; import './projectActivity.css'; import ProjectActivityAnalysesList from './ProjectActivityAnalysesList'; import ProjectActivityGraphs from './ProjectActivityGraphs'; @@ -32,7 +32,7 @@ import ProjectActivityPageHeader from './ProjectActivityPageHeader'; interface Props { addCustomEvent: (analysis: string, name: string, category?: string) => Promise; addVersion: (analysis: string, version: string) => Promise; - analyses: ParsedAnalysis[]; + analyses: T.ParsedAnalysis[]; analysesLoading: boolean; changeEvent: (event: string, name: string) => Promise; deleteAnalysis: (analysis: string) => Promise; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx index 7283ba97355..ce0e6a6196a 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx @@ -33,7 +33,6 @@ import { getProjectActivityGraph, isCustomGraph, MeasureHistory, - ParsedAnalysis, parseQuery, Query, serializeQuery, @@ -49,7 +48,7 @@ interface Props { } export interface State { - analyses: ParsedAnalysis[]; + analyses: T.ParsedAnalysis[]; analysesLoading: boolean; graphLoading: boolean; initialized: boolean; @@ -158,7 +157,7 @@ export default class ProjectActivityAppContainer extends React.PureComponent ({ ...analysis, date: parseDate(analysis.date) - })) as ParsedAnalysis[], + })) as T.ParsedAnalysis[], paging })); }; @@ -204,8 +203,8 @@ export default class ProjectActivityAppContainer extends React.PureComponent => { + prevResult?: { analyses: T.ParsedAnalysis[]; paging: T.Paging } + ): Promise<{ analyses: T.ParsedAnalysis[]; paging: T.Paging }> => { if ( prevResult && prevResult.paging.pageIndex * prevResult.paging.pageSize >= prevResult.paging.total diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx index 2f7ac01cbda..d2c510a4a51 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx @@ -29,7 +29,6 @@ import { historyQueryChanged, isCustomGraph, MeasureHistory, - ParsedAnalysis, Point, PROJECT_ACTIVITY_GRAPH, PROJECT_ACTIVITY_GRAPH_CUSTOM, @@ -42,7 +41,7 @@ import GraphsZoom from './GraphsZoom'; import ProjectActivityGraphsHeader from './ProjectActivityGraphsHeader'; interface Props { - analyses: ParsedAnalysis[]; + analyses: T.ParsedAnalysis[]; leakPeriodDate?: Date; loading: boolean; measuresHistory: MeasureHistory[]; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx index 3b0c9ff0e54..679cce88b1e 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx @@ -20,12 +20,11 @@ import * as React from 'react'; import ConfirmModal from 'sonar-ui-common/components/controls/ConfirmModal'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { ParsedAnalysis } from '../../utils'; interface Props { addEvent: (analysis: string, name: string, category?: string) => Promise; addEventButtonText: string; - analysis: ParsedAnalysis; + analysis: T.ParsedAnalysis; onClose: () => void; } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx index c3dee2cac2d..36fdc8d723d 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx @@ -20,10 +20,9 @@ import * as React from 'react'; import ConfirmModal from 'sonar-ui-common/components/controls/ConfirmModal'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { ParsedAnalysis } from '../../utils'; interface Props { - analysis: ParsedAnalysis; + analysis: T.ParsedAnalysis; deleteAnalysis: (analysis: string) => Promise; onClose: () => void; } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.ts b/server/sonar-web/src/main/js/apps/projectActivity/utils.ts index 7d8c774edd2..82dd6bdd1bc 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/utils.ts +++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.ts @@ -32,8 +32,6 @@ import { } from 'sonar-ui-common/helpers/query'; import { get } from 'sonar-ui-common/helpers/storage'; -export type ParsedAnalysis = T.Omit & { date: Date }; - export interface Query { category: string; customMetrics: string[]; @@ -193,12 +191,12 @@ export function getSeriesMetricType(series: Serie[]) { } interface AnalysesByDay { - byDay: T.Dict; + byDay: T.Dict; version: string | null; key: string | null; } -export function getAnalysesByVersionByDay(analyses: ParsedAnalysis[], query: Query) { +export function getAnalysesByVersionByDay(analyses: T.ParsedAnalysis[], query: Query) { return analyses.reduce((acc, analysis) => { let currentVersion = acc[acc.length - 1]; const versionEvent = analysis.events.find(event => event.category === 'VERSION'); diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx index 32b7edf81da..84c2a008476 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx @@ -93,6 +93,12 @@ it('should handle errors gracefully', async () => { function shallowRender(props: Partial = {}) { return shallow( - + ); } diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx index 230a2ec74c9..abe14e18235 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx @@ -88,26 +88,6 @@ it('should disable the save button when date is invalid', () => { ).toBe(true); }); -describe('getSettingValue', () => { - const wrapper = shallowRender(); - wrapper.setState({ analysis: 'analysis1', days: '35' }); - - it('should work for Days', () => { - wrapper.setState({ selected: 'NUMBER_OF_DAYS' }); - expect(wrapper.instance().getSettingValue()).toBe('35'); - }); - - it('should work for Analysis', () => { - wrapper.setState({ selected: 'SPECIFIC_ANALYSIS' }); - expect(wrapper.instance().getSettingValue()).toBe('analysis1'); - }); - - it('should work for Previous version', () => { - wrapper.setState({ selected: 'PREVIOUS_VERSION' }); - expect(wrapper.instance().getSettingValue()).toBeUndefined(); - }); -}); - function shallowRender(props: Partial = {}) { return shallow( { expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot(); }); it('should not show save button when unchanged', () => { @@ -32,7 +33,12 @@ it('should not show save button when unchanged', () => { currentSetting: 'PREVIOUS_VERSION', selected: 'PREVIOUS_VERSION' }); - expect(wrapper.find('SubmitButton')).toHaveLength(0); + expect( + wrapper + .find('SubmitButton') + .parent() + .hasClass('invisible') + ).toBe(true); }); it('should show save button when changed', () => { @@ -84,7 +90,10 @@ it('should disable the save button when date is invalid', () => { function shallowRender(props: Partial = {}) { return shallow(
- + + +
+
+
+

+ baseline.next_analysis_notice +

+ - + save + +
+ +`; + +exports[`should render correctly 2`] = ` +
+
+
+ + + +
+
+
+

+ baseline.next_analysis_notice +

+ + + save +
`; diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts new file mode 100644 index 00000000000..c77f816b373 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { getSettingValue } from '../utils'; + +describe('getSettingValue', () => { + it('should work for Days', () => { + expect(getSettingValue({ analysis: 'analysis', days: '35', type: 'NUMBER_OF_DAYS' })).toBe( + '35' + ); + }); + + it('should work for Analysis', () => { + expect(getSettingValue({ analysis: 'analysis1', days: '35', type: 'SPECIFIC_ANALYSIS' })).toBe( + 'analysis1' + ); + }); + + it('should work for Previous version', () => { + expect( + getSettingValue({ analysis: 'analysis1', days: '35', type: 'PREVIOUS_VERSION' }) + ).toBeUndefined(); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx index c88e2fc9e64..816c0805985 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx @@ -25,16 +25,19 @@ import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import { getNewCodePeriod, resetNewCodePeriod, setNewCodePeriod } from '../../../api/newCodePeriod'; import '../styles.css'; +import { getSettingValue } from '../utils'; import BranchList from './BranchList'; import ProjectBaselineSelector from './ProjectBaselineSelector'; interface Props { branchLikes: T.BranchLike[]; + branchesEnabled?: boolean; canAdmin?: boolean; component: T.Component; } interface State { + analysis?: string; currentSetting?: T.NewCodePeriodSettingType; currentSettingValue?: string; days: string; @@ -65,9 +68,34 @@ export default class App extends React.PureComponent { this.mounted = false; } + getUpdatedState(params: { + currentSetting?: T.NewCodePeriodSettingType; + currentSettingValue?: string; + generalSetting: T.NewCodePeriod; + }) { + const { currentSetting, currentSettingValue, generalSetting } = params; + + return { + loading: false, + currentSetting, + currentSettingValue, + generalSetting, + selected: currentSetting, + days: currentSetting === 'NUMBER_OF_DAYS' ? currentSettingValue || '30' : '', + analysis: (currentSetting === 'SPECIFIC_ANALYSIS' && currentSettingValue) || '' + }; + } + fetchLeakPeriodSetting() { this.setState({ loading: true }); - Promise.all([getNewCodePeriod(), getNewCodePeriod({ project: this.props.component.key })]).then( + + Promise.all([ + getNewCodePeriod(), + getNewCodePeriod({ + branch: this.props.branchesEnabled ? 'master' : undefined, + project: this.props.component.key + }) + ]).then( ([generalSetting, setting]) => { if (this.mounted) { if (!generalSetting.type) { @@ -75,22 +103,10 @@ export default class App extends React.PureComponent { } const currentSettingValue = setting.value; const currentSetting = setting.inherited ? undefined : setting.type || 'PREVIOUS_VERSION'; - const newState = { - loading: false, - currentSetting, - currentSettingValue, - generalSetting, - selected: currentSetting - }; - if (currentSetting === 'NUMBER_OF_DAYS') { - this.setState({ - days: currentSettingValue || '30', - ...newState - }); - } else { - this.setState(newState); - } + this.setState( + this.getUpdatedState({ generalSetting, currentSetting, currentSettingValue }) + ); } }, () => { @@ -115,6 +131,8 @@ export default class App extends React.PureComponent { ); }; + handleSelectAnalysis = (analysis: T.ParsedAnalysis) => this.setState({ analysis: analysis.key }); + handleSelectDays = (days: string) => this.setState({ days }); handleSelectSetting = (selected?: T.NewCodePeriodSettingType) => this.setState({ selected }); @@ -123,10 +141,9 @@ export default class App extends React.PureComponent { e.preventDefault(); const { component } = this.props; - const { days, selected } = this.state; + const { analysis, days, selected: type } = this.state; - const type = selected; - const value = type === 'NUMBER_OF_DAYS' ? days : undefined; + const value = getSettingValue({ type, analysis, days }); if (type) { this.setState({ saving: true }); @@ -196,7 +213,9 @@ export default class App extends React.PureComponent { } render() { + const { branchLikes, branchesEnabled, component } = this.props; const { + analysis, currentSetting, days, generalSetting, @@ -213,21 +232,23 @@ export default class App extends React.PureComponent { ) : (
-

{translate('project_baseline.default_setting')}

-

{translate('project_baseline.default_setting.description')}

+ {branchesEnabled && ( + <> +

{translate('project_baseline.default_setting')}

+

{translate('project_baseline.default_setting.description')}

+ + )} {generalSetting && (
{currentSetting && ( <> - )} -
+
{translate('project_baseline.general_setting')}: {this.renderGeneralSetting(generalSetting)}
@@ -235,19 +256,23 @@ export default class App extends React.PureComponent { )} - {generalSetting && ( + {generalSetting && branchesEnabled && ( ({ + branchesEnabled: getAppState(state).branchesEnabled, canAdmin: getAppState(state).canAdmin }); diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx index 4791f62ba6e..b2ed30fd434 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx @@ -30,17 +30,17 @@ import { getProjectActivity } from '../../../api/projectActivity'; import DateFormatter from '../../../components/intl/DateFormatter'; import TimeFormatter from '../../../components/intl/TimeFormatter'; import Events from '../../projectActivity/components/Events'; -import { getAnalysesByVersionByDay, ParsedAnalysis } from '../../projectActivity/utils'; +import { getAnalysesByVersionByDay } from '../../projectActivity/utils'; interface Props { analysis: string; branch: string; component: string; - onSelectAnalysis: (analysis: ParsedAnalysis) => void; + onSelectAnalysis: (analysis: T.ParsedAnalysis) => void; } interface State { - analyses: ParsedAnalysis[]; + analyses: T.ParsedAnalysis[]; loading: boolean; range: number; scroll: number; @@ -90,7 +90,7 @@ export default class BranchAnalysisList extends React.PureComponent ({ ...analysis, date: parseDate(analysis.date) - })) as ParsedAnalysis[], + })) as T.ParsedAnalysis[], loading: false }); }); diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx index b8dd08c6b66..0a180907f92 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx @@ -24,8 +24,7 @@ import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { toNotSoISOString } from 'sonar-ui-common/helpers/dates'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import { setNewCodePeriod } from '../../../api/newCodePeriod'; -import { ParsedAnalysis } from '../../projectActivity/utils'; -import { validateDays } from '../utils'; +import { getSettingValue, validateSetting } from '../utils'; import BaselineSettingAnalysis from './BaselineSettingAnalysis'; import BaselineSettingDays from './BaselineSettingDays'; import BaselineSettingPreviousVersion from './BaselineSettingPreviousVersion'; @@ -73,24 +72,13 @@ export default class BranchBaselineSettingModal extends React.PureComponent) => { e.preventDefault(); const { branch, component } = this.props; - const { analysisDate, selected: type } = this.state; + const { analysis, analysisDate, days, selected: type } = this.state; - const value = this.getSettingValue(); + const value = getSettingValue({ type, analysis, days }); if (type) { this.setState({ saving: true }); @@ -121,7 +109,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent this.props.onClose(); - handleSelectAnalysis = (analysis: ParsedAnalysis) => + handleSelectAnalysis = (analysis: T.ParsedAnalysis) => this.setState({ analysis: analysis.key, analysisDate: analysis.date }); handleSelectDays = (days: string) => this.setState({ days }); @@ -132,20 +120,18 @@ export default class BranchBaselineSettingModal extends React.PureComponent 0) || - (selected === 'NUMBER_OF_DAYS' && validateDays(days)); + const { isChanged, isValid } = validateSetting({ + analysis, + currentSetting, + currentSettingValue, + days, + selected + }); return ( @@ -153,7 +139,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent{header}
-
+
void; onSelectDays: (value: string) => void; onSelectSetting: (value: T.NewCodePeriodSettingType) => void; onSubmit: (e: React.SyntheticEvent) => void; @@ -37,37 +44,62 @@ export interface ProjectBaselineSelectorProps { } export default function ProjectBaselineSelector(props: ProjectBaselineSelectorProps) { - const { currentSetting, days, currentSettingValue, saving, selected } = props; + const { + analysis, + branchesEnabled, + component, + currentSetting, + days, + currentSettingValue, + saving, + selected + } = props; - const isChanged = - selected !== currentSetting || - (selected === 'NUMBER_OF_DAYS' && String(days) !== currentSettingValue); - - const isValid = selected !== 'NUMBER_OF_DAYS' || validateDays(days); + const { isChanged, isValid } = validateSetting({ + analysis, + currentSetting, + currentSettingValue, + days, + selected + }); return ( -
- - -
- {isChanged && ( -
-

{translate('baseline.next_analysis_notice')}

- - {translate('save')} +
+
+ + + {!branchesEnabled && ( + + )}
- )} + {selected === 'SPECIFIC_ANALYSIS' && ( + + )} +
+
+

{translate('baseline.next_analysis_notice')}

+ + {translate('save')} +
); } diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css index 06b52b4a9bc..9bffdfa35cc 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css +++ b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css @@ -18,13 +18,12 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -.project-baseline-selector { - height: 300px; +.project-baseline-selector > .branch-baseline-setting-modal { + max-height: 60vh; + padding-top: 2px; } .branch-baseline-setting-modal { - height: 60vh; - overflow: hidden; display: flex; flex-direction: column; } @@ -34,6 +33,7 @@ flex-direction: column; overflow: hidden; position: relative; + min-height: 200px; } .branch-analysis-list { @@ -100,10 +100,6 @@ z-index: var(--belowNormalZIndex); } -.branch-analysis-version-badge.sticky + .branch-analysis-days-list { - padding-top: 36px; -} - .branch-analysis-version-badge .badge { max-width: 385px; border-radius: 0 2px 2px 0; @@ -133,3 +129,7 @@ .project-activity-event-icon.OTHER { color: #442d1b; } + +.invisible { + visibility: hidden; +} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts index afaa962b3dc..69b7cfabda0 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts +++ b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts @@ -22,3 +22,44 @@ export function validateDays(days: string) { return !(days.length < 1 || isNaN(parsed) || parsed < 1 || String(parsed) !== days); } + +export function getSettingValue({ + analysis, + days, + type +}: { + analysis?: string; + days?: string; + type?: T.NewCodePeriodSettingType; +}) { + switch (type) { + case 'NUMBER_OF_DAYS': + return days; + case 'SPECIFIC_ANALYSIS': + return analysis; + default: + return undefined; + } +} + +export function validateSetting(state: { + analysis?: string; + currentSetting?: T.NewCodePeriodSettingType; + currentSettingValue?: string; + days: string; + selected?: T.NewCodePeriodSettingType; +}) { + const { analysis = '', currentSetting, currentSettingValue, days, selected } = state; + + const isChanged = + selected !== currentSetting || + (selected === 'NUMBER_OF_DAYS' && days !== currentSettingValue) || + (selected === 'SPECIFIC_ANALYSIS' && analysis !== currentSettingValue); + + const isValid = + selected === 'PREVIOUS_VERSION' || + (selected === 'SPECIFIC_ANALYSIS' && analysis.length > 0) || + (selected === 'NUMBER_OF_DAYS' && validateDays(days)); + + return { isChanged, isValid }; +} diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts index 0b159181b44..c667b17d2d1 100644 --- a/server/sonar-web/src/main/js/helpers/testMocks.ts +++ b/server/sonar-web/src/main/js/helpers/testMocks.ts @@ -22,7 +22,6 @@ import { Location } from 'history'; import { InjectedRouter } from 'react-router'; import { createStore, Store } from 'redux'; import { DocumentationEntry } from '../apps/documentation/utils'; -import { ParsedAnalysis } from '../apps/projectActivity/utils'; import { Profile } from '../apps/quality-profiles/types'; export function mockAlmApplication(overrides: Partial = {}): T.AlmApplication { @@ -61,7 +60,7 @@ export function mockAnalysis(overrides: Partial = {}): T.Analysis { }; } -export function mockParsedAnalysis(overrides: Partial = {}): ParsedAnalysis { +export function mockParsedAnalysis(overrides: Partial = {}): T.ParsedAnalysis { return { date: new Date('2017-03-01T09:37:01+0100'), events: [], -- cgit v1.2.3