diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2019-08-30 17:31:15 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-09-24 20:21:17 +0200 |
commit | 3079907d1f13a509709c72a5dade87d129a4eb5c (patch) | |
tree | d3a79825cbf8ae25a3f473f5cfc07ffbfd36eba2 /server/sonar-web/src/main/js/apps/projectBaseline | |
parent | 45f8d835abdaf01287e2cf4a149b87ab2abfd156 (diff) | |
download | sonarqube-3079907d1f13a509709c72a5dade87d129a4eb5c.tar.gz sonarqube-3079907d1f13a509709c72a5dade87d129a4eb5c.zip |
SONAR-12430 Handle project baseline for Community Edition
Diffstat (limited to 'server/sonar-web/src/main/js/apps/projectBaseline')
13 files changed, 326 insertions, 131 deletions
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<App['props']> = {}) { return shallow<App>( - <App branchLikes={[]} canAdmin={true} component={mockComponent()} {...props} /> + <App + branchLikes={[]} + branchesEnabled={true} + canAdmin={true} + component={mockComponent()} + {...props} + /> ); } 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<BranchBaselineSettingModal['props']> = {}) { return shallow<BranchBaselineSettingModal>( <BranchBaselineSettingModal diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx index 87608358874..74384231bf2 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx @@ -25,6 +25,7 @@ import ProjectBaselineSelector, { it('should render correctly', () => { 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<ProjectBaselineSelectorProps> = {}) { return shallow( <ProjectBaselineSelector + branchesEnabled={true} + component="" days="12" + onSelectAnalysis={jest.fn()} onSelectDays={jest.fn()} onSelectSetting={jest.fn()} onSubmit={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap index a9710d794a4..e9ffc30753c 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchBaselineSettingModal-test.tsx.snap @@ -17,7 +17,7 @@ exports[`should render correctly 1`] = ` onSubmit={[Function]} > <div - className="modal-body branch-baseline-setting-modal" + className="modal-body modal-container branch-baseline-setting-modal" > <div className="display-flex-row huge-spacer-bottom" diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap index c8fe6992237..d938d19be84 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap @@ -6,21 +6,96 @@ exports[`should render correctly 1`] = ` onSubmit={[MockFunction]} > <div - className="display-flex-row big-spacer-bottom" - role="radiogroup" + className="branch-baseline-setting-modal" > - <BaselineSettingPreviousVersion - onSelect={[MockFunction]} - selected={false} + <div + className="display-flex-row big-spacer-bottom" + role="radiogroup" + > + <BaselineSettingPreviousVersion + onSelect={[MockFunction]} + selected={false} + /> + <BaselineSettingDays + days="12" + isChanged={false} + isValid={false} + onChangeDays={[MockFunction]} + onSelect={[MockFunction]} + selected={false} + /> + </div> + </div> + <div + className="big-spacer-top invisible" + > + <p + className="spacer-bottom" + > + baseline.next_analysis_notice + </p> + <DeferredSpinner + className="spacer-right" + loading={false} + timeout={100} /> - <BaselineSettingDays - days="12" - isChanged={false} - isValid={true} - onChangeDays={[MockFunction]} - onSelect={[MockFunction]} - selected={false} + <SubmitButton + disabled={true} + > + save + </SubmitButton> + </div> +</form> +`; + +exports[`should render correctly 2`] = ` +<form + className="project-baseline-selector" + onSubmit={[MockFunction]} +> + <div + className="branch-baseline-setting-modal" + > + <div + className="display-flex-row big-spacer-bottom" + role="radiogroup" + > + <BaselineSettingPreviousVersion + onSelect={[MockFunction]} + selected={false} + /> + <BaselineSettingDays + days="12" + isChanged={false} + isValid={false} + onChangeDays={[MockFunction]} + onSelect={[MockFunction]} + selected={false} + /> + <BaselineSettingAnalysis + onSelect={[MockFunction]} + selected={false} + /> + </div> + </div> + <div + className="big-spacer-top invisible" + > + <p + className="spacer-bottom" + > + baseline.next_analysis_notice + </p> + <DeferredSpinner + className="spacer-right" + loading={false} + timeout={100} /> + <SubmitButton + disabled={true} + > + save + </SubmitButton> </div> </form> `; 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<Props, State> { 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<Props, State> { } 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<Props, State> { ); }; + 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<Props, State> { 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<Props, State> { } render() { + const { branchLikes, branchesEnabled, component } = this.props; const { + analysis, currentSetting, days, generalSetting, @@ -213,21 +232,23 @@ export default class App extends React.PureComponent<Props, State> { <DeferredSpinner /> ) : ( <div className="panel panel-white"> - <h2>{translate('project_baseline.default_setting')}</h2> - <p>{translate('project_baseline.default_setting.description')}</p> + {branchesEnabled && ( + <> + <h2>{translate('project_baseline.default_setting')}</h2> + <p>{translate('project_baseline.default_setting.description')}</p> + </> + )} {generalSetting && ( <div className="text-right spacer-bottom"> {currentSetting && ( <> - <Button - className="spacer-right little-spacer-bottom" - onClick={this.resetSetting}> + <Button className="little-spacer-bottom" onClick={this.resetSetting}> {translate('project_baseline.reset_to_general')} </Button> </> )} - <div className="spacer-top spacer-right medium"> + <div className="spacer-top medium"> <strong>{translate('project_baseline.general_setting')}: </strong> {this.renderGeneralSetting(generalSetting)} </div> @@ -235,19 +256,23 @@ export default class App extends React.PureComponent<Props, State> { )} <ProjectBaselineSelector + analysis={analysis} + branchesEnabled={branchesEnabled} + component={component.key} currentSetting={currentSetting} currentSettingValue={currentSettingValue} days={days} + onSelectAnalysis={this.handleSelectAnalysis} onSelectDays={this.handleSelectDays} onSelectSetting={this.handleSelectSetting} onSubmit={this.handleSubmit} saving={saving} selected={selected} /> - {generalSetting && ( + {generalSetting && branchesEnabled && ( <BranchList - branchLikes={this.props.branchLikes} - component={this.props.component} + branchLikes={branchLikes} + component={component} inheritedSetting={ currentSetting ? { diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts b/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts index 10205a7f932..1a38ec7f7f8 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts @@ -22,6 +22,7 @@ import { getAppState, Store } from '../../../store/rootReducer'; import App from './App'; const mapStateToProps = (state: Store) => ({ + 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<Props, State analyses: result.analyses.map(analysis => ({ ...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<Prop : null; } - getSettingValue() { - switch (this.state.selected) { - case 'NUMBER_OF_DAYS': - return this.state.days; - case 'SPECIFIC_ANALYSIS': - return this.state.analysis; - default: - return undefined; - } - } - handleSubmit = (e: React.SyntheticEvent<HTMLFormElement>) => { 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<Prop requestClose = () => 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<Prop const { branch } = this.props; const { analysis, days, saving, selected } = this.state; - const currentSetting = branch.newCodePeriod && branch.newCodePeriod.type; - const currentSettingValue = branch.newCodePeriod && branch.newCodePeriod.value; - const header = translateWithParameters('baseline.new_code_period_for_branch_x', branch.name); - const isChanged = - selected !== currentSetting || - (selected === 'NUMBER_OF_DAYS' && String(days) !== currentSettingValue) || - (selected === 'SPECIFIC_ANALYSIS' && analysis !== currentSettingValue); + const currentSetting = branch.newCodePeriod && branch.newCodePeriod.type; + const currentSettingValue = branch.newCodePeriod && branch.newCodePeriod.value; - const isValid = - selected === 'PREVIOUS_VERSION' || - (selected === 'SPECIFIC_ANALYSIS' && analysis.length > 0) || - (selected === 'NUMBER_OF_DAYS' && validateDays(days)); + const { isChanged, isValid } = validateSetting({ + analysis, + currentSetting, + currentSettingValue, + days, + selected + }); return ( <Modal contentLabel={header} onRequestClose={this.requestClose} size="large"> @@ -153,7 +139,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop <h2>{header}</h2> </header> <form onSubmit={this.handleSubmit}> - <div className="modal-body branch-baseline-setting-modal"> + <div className="modal-body modal-container branch-baseline-setting-modal"> <div className="display-flex-row huge-spacer-bottom" role="radiogroup"> <BaselineSettingPreviousVersion isDefault={false} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx index a29dfb03eba..5aabd1ad5ac 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx @@ -17,18 +17,25 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import * as classNames from 'classnames'; import * as React from 'react'; import { SubmitButton } from 'sonar-ui-common/components/controls/buttons'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { validateDays } from '../utils'; +import { validateSetting } from '../utils'; +import BaselineSettingAnalysis from './BaselineSettingAnalysis'; import BaselineSettingDays from './BaselineSettingDays'; import BaselineSettingPreviousVersion from './BaselineSettingPreviousVersion'; +import BranchAnalysisList from './BranchAnalysisList'; export interface ProjectBaselineSelectorProps { + analysis?: string; + branchesEnabled?: boolean; + component: string; currentSetting?: T.NewCodePeriodSettingType; - currentSettingValue?: string | number; + currentSettingValue?: string; days: string; + onSelectAnalysis: (analysis: T.ParsedAnalysis) => void; onSelectDays: (value: string) => void; onSelectSetting: (value: T.NewCodePeriodSettingType) => void; onSubmit: (e: React.SyntheticEvent<HTMLFormElement>) => 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 ( <form className="project-baseline-selector" onSubmit={props.onSubmit}> - <div className="display-flex-row big-spacer-bottom" role="radiogroup"> - <BaselineSettingPreviousVersion - onSelect={props.onSelectSetting} - selected={selected === 'PREVIOUS_VERSION'} - /> - <BaselineSettingDays - days={days} - isChanged={isChanged} - isValid={isValid} - onChangeDays={props.onSelectDays} - onSelect={props.onSelectSetting} - selected={selected === 'NUMBER_OF_DAYS'} - /> - </div> - {isChanged && ( - <div> - <p className="spacer-bottom">{translate('baseline.next_analysis_notice')}</p> - <DeferredSpinner className="spacer-right" loading={saving} /> - <SubmitButton disabled={saving || !isValid}>{translate('save')}</SubmitButton> + <div className="branch-baseline-setting-modal"> + <div className="display-flex-row big-spacer-bottom" role="radiogroup"> + <BaselineSettingPreviousVersion + onSelect={props.onSelectSetting} + selected={selected === 'PREVIOUS_VERSION'} + /> + <BaselineSettingDays + days={days} + isChanged={isChanged} + isValid={isValid} + onChangeDays={props.onSelectDays} + onSelect={props.onSelectSetting} + selected={selected === 'NUMBER_OF_DAYS'} + /> + {!branchesEnabled && ( + <BaselineSettingAnalysis + onSelect={props.onSelectSetting} + selected={selected === 'SPECIFIC_ANALYSIS'} + /> + )} </div> - )} + {selected === 'SPECIFIC_ANALYSIS' && ( + <BranchAnalysisList + analysis={analysis || ''} + branch="master" + component={component} + onSelectAnalysis={props.onSelectAnalysis} + /> + )} + </div> + <div className={classNames('big-spacer-top', { invisible: !isChanged })}> + <p className="spacer-bottom">{translate('baseline.next_analysis_notice')}</p> + <DeferredSpinner className="spacer-right" loading={saving} /> + <SubmitButton disabled={saving || !isValid || !isChanged}>{translate('save')}</SubmitButton> + </div> </form> ); } 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 }; +} |