aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorJacek <jacek.poreda@sonarsource.com>2019-09-06 10:19:26 +0200
committerSonarTech <sonartech@sonarsource.com>2019-09-24 20:21:17 +0200
commite8f1a84869bcb72af1ca30fa78b9c936a3e028a3 (patch)
treec525ec29534c2266bd7cc76cfab344cfa6e1c3ae /server/sonar-web
parent3079907d1f13a509709c72a5dade87d129a4eb5c (diff)
downloadsonarqube-e8f1a84869bcb72af1ca30fa78b9c936a3e028a3.tar.gz
sonarqube-e8f1a84869bcb72af1ca30fa78b9c936a3e028a3.zip
Fix conflicts after rebase
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/App-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/ProjectBaselineSelector-test.tsx31
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap15
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/ProjectBaselineSelector-test.tsx.snap170
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/utils-test.ts77
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx120
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingPreviousVersion.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx68
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/styles.css17
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/utils.ts25
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx12
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts15
-rw-r--r--server/sonar-web/src/main/js/helpers/periods.ts2
18 files changed, 478 insertions, 153 deletions
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 25171679c5c..ff168b99032 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
@@ -157,10 +157,7 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
};
shouldRenderBaselineMarker(analysis: T.ParsedAnalysis): boolean {
- return Boolean(
- analysis.manualNewCodePeriodBaseline ||
- (this.props.leakPeriodDate && isEqual(this.props.leakPeriodDate, analysis.date))
- );
+ return Boolean(this.props.leakPeriodDate && isEqual(this.props.leakPeriodDate, analysis.date));
}
renderAnalysis(analysis: T.ParsedAnalysis) {
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 84c2a008476..1e1b5faf34f 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
@@ -40,15 +40,6 @@ it('should not display reset button if project setting is not set', () => {
expect(wrapper.find('Button')).toHaveLength(0);
});
-it('should display reset button if project setting is set', async () => {
- (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '27' });
-
- const wrapper = shallowRender();
-
- await waitAndUpdate(wrapper);
- expect(wrapper.find('Button')).toHaveLength(1);
-});
-
it('should reset the setting correctly', async () => {
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
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 74384231bf2..68b96d50a0e 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,13 +25,22 @@ import ProjectBaselineSelector, {
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
- expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot();
+ expect(
+ shallowRender({
+ branchesEnabled: false,
+ generalSetting: { type: 'NUMBER_OF_DAYS', value: '23' }
+ })
+ ).toMatchSnapshot();
+ expect(
+ shallowRender({ branchesEnabled: false, generalSetting: { type: 'NUMBER_OF_DAYS', value: '' } })
+ ).toMatchSnapshot();
});
it('should not show save button when unchanged', () => {
const wrapper = shallowRender({
currentSetting: 'PREVIOUS_VERSION',
- selected: 'PREVIOUS_VERSION'
+ selected: 'PREVIOUS_VERSION',
+ overrideGeneralSetting: true
});
expect(
wrapper
@@ -42,7 +51,11 @@ it('should not show save button when unchanged', () => {
});
it('should show save button when changed', () => {
- const wrapper = shallowRender({ currentSetting: 'PREVIOUS_VERSION', selected: 'NUMBER_OF_DAYS' });
+ const wrapper = shallowRender({
+ currentSetting: 'PREVIOUS_VERSION',
+ selected: 'NUMBER_OF_DAYS',
+ overrideGeneralSetting: true
+ });
expect(wrapper.find('SubmitButton')).toHaveLength(1);
});
@@ -51,7 +64,8 @@ it('should show save button when value changed', () => {
currentSetting: 'NUMBER_OF_DAYS',
currentSettingValue: '23',
days: '25',
- selected: 'NUMBER_OF_DAYS'
+ selected: 'NUMBER_OF_DAYS',
+ overrideGeneralSetting: true
});
expect(wrapper.find('SubmitButton')).toHaveLength(1);
});
@@ -61,7 +75,8 @@ it('should disable the save button when saving', () => {
currentSetting: 'NUMBER_OF_DAYS',
currentSettingValue: '25',
saving: true,
- selected: 'PREVIOUS_VERSION'
+ selected: 'PREVIOUS_VERSION',
+ overrideGeneralSetting: true
});
expect(
@@ -76,7 +91,8 @@ it('should disable the save button when date is invalid', () => {
const wrapper = shallowRender({
currentSetting: 'PREVIOUS_VERSION',
days: 'hello',
- selected: 'NUMBER_OF_DAYS'
+ selected: 'NUMBER_OF_DAYS',
+ overrideGeneralSetting: true
});
expect(
@@ -93,10 +109,13 @@ function shallowRender(props: Partial<ProjectBaselineSelectorProps> = {}) {
branchesEnabled={true}
component=""
days="12"
+ generalSetting={{}}
onSelectAnalysis={jest.fn()}
onSelectDays={jest.fn()}
onSelectSetting={jest.fn()}
onSubmit={jest.fn()}
+ onToggleSpecificSetting={jest.fn()}
+ overrideGeneralSetting={false}
saving={false}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap
index 571628b3fda..8d070004181 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap
@@ -11,11 +11,6 @@ exports[`should render correctly 1`] = `
branch_list.branch
</th>
<th
- className="thin"
- >
-
- </th>
- <th
className="thin nowrap huge-spacer-right"
>
branch_list.current_setting
@@ -56,7 +51,6 @@ exports[`should render correctly 1`] = `
branches.main_branch
</div>
</td>
- <td />
<td
className="huge-spacer-right nowrap"
>
@@ -98,17 +92,10 @@ exports[`should render correctly 1`] = `
/>
branch-6.7
</td>
- <td>
- <span
- className="badge badge-info"
- >
- default
- </span>
- </td>
<td
className="huge-spacer-right nowrap"
>
- baseline.previous_version
+ branch_list.default_setting
</td>
<td
className="text-right"
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 d938d19be84..215bbdd3965 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,20 +6,56 @@ exports[`should render correctly 1`] = `
onSubmit={[MockFunction]}
>
<div
- className="branch-baseline-setting-modal"
+ className="big-spacer-top spacer-bottom"
+ role="radiogroup"
+ >
+ <Radio
+ checked={true}
+ className="big-spacer-bottom"
+ onCheck={[Function]}
+ value="general"
+ >
+ project_baseline.general_setting
+ </Radio>
+ <div
+ className="big-spacer-left"
+ >
+ <div
+ className="general-setting"
+ >
+ <strong>
+ baseline.previous_version
+ </strong>
+ :
+ baseline.previous_version.description
+ </div>
+ </div>
+ <Radio
+ checked={false}
+ className="huge-spacer-top"
+ onCheck={[Function]}
+ value="specific"
+ >
+ project_baseline.specific_setting
+ </Radio>
+ </div>
+ <div
+ className="big-spacer-left big-spacer-right branch-baseline-setting-modal"
>
<div
className="display-flex-row big-spacer-bottom"
role="radiogroup"
>
<BaselineSettingPreviousVersion
+ disabled={true}
onSelect={[MockFunction]}
selected={false}
/>
<BaselineSettingDays
days="12"
+ disabled={true}
isChanged={false}
- isValid={false}
+ isValid={true}
onChangeDays={[MockFunction]}
onSelect={[MockFunction]}
selected={false}
@@ -54,25 +90,151 @@ exports[`should render correctly 2`] = `
onSubmit={[MockFunction]}
>
<div
- className="branch-baseline-setting-modal"
+ className="big-spacer-top spacer-bottom"
+ role="radiogroup"
+ >
+ <Radio
+ checked={true}
+ className="big-spacer-bottom"
+ onCheck={[Function]}
+ value="general"
+ >
+ project_baseline.general_setting
+ </Radio>
+ <div
+ className="big-spacer-left"
+ >
+ <div
+ className="general-setting"
+ >
+ <strong>
+ baseline.number_days (duration.days.23)
+ </strong>
+ :
+ baseline.number_days.description
+ </div>
+ </div>
+ <Radio
+ checked={false}
+ className="huge-spacer-top"
+ onCheck={[Function]}
+ value="specific"
+ >
+ project_baseline.specific_setting
+ </Radio>
+ </div>
+ <div
+ className="big-spacer-left big-spacer-right branch-baseline-setting-modal"
+ >
+ <div
+ className="display-flex-row big-spacer-bottom"
+ role="radiogroup"
+ >
+ <BaselineSettingPreviousVersion
+ disabled={true}
+ onSelect={[MockFunction]}
+ selected={false}
+ />
+ <BaselineSettingDays
+ days="12"
+ disabled={true}
+ isChanged={false}
+ isValid={true}
+ onChangeDays={[MockFunction]}
+ onSelect={[MockFunction]}
+ selected={false}
+ />
+ <BaselineSettingAnalysis
+ disabled={true}
+ 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>
+`;
+
+exports[`should render correctly 3`] = `
+<form
+ className="project-baseline-selector"
+ onSubmit={[MockFunction]}
+>
+ <div
+ className="big-spacer-top spacer-bottom"
+ role="radiogroup"
+ >
+ <Radio
+ checked={true}
+ className="big-spacer-bottom"
+ onCheck={[Function]}
+ value="general"
+ >
+ project_baseline.general_setting
+ </Radio>
+ <div
+ className="big-spacer-left"
+ >
+ <div
+ className="general-setting"
+ >
+ <strong>
+ baseline.number_days (duration.days.?)
+ </strong>
+ :
+ baseline.number_days.description
+ </div>
+ </div>
+ <Radio
+ checked={false}
+ className="huge-spacer-top"
+ onCheck={[Function]}
+ value="specific"
+ >
+ project_baseline.specific_setting
+ </Radio>
+ </div>
+ <div
+ className="big-spacer-left big-spacer-right branch-baseline-setting-modal"
>
<div
className="display-flex-row big-spacer-bottom"
role="radiogroup"
>
<BaselineSettingPreviousVersion
+ disabled={true}
onSelect={[MockFunction]}
selected={false}
/>
<BaselineSettingDays
days="12"
+ disabled={true}
isChanged={false}
- isValid={false}
+ isValid={true}
onChangeDays={[MockFunction]}
onSelect={[MockFunction]}
selected={false}
/>
<BaselineSettingAnalysis
+ disabled={true}
onSelect={[MockFunction]}
selected={false}
/>
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
index c77f816b373..a06699d2261 100644
--- 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
@@ -17,7 +17,7 @@
* 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';
+import { getSettingValue, validateSetting } from '../utils';
describe('getSettingValue', () => {
it('should work for Days', () => {
@@ -38,3 +38,78 @@ describe('getSettingValue', () => {
).toBeUndefined();
});
});
+
+describe('validateSettings', () => {
+ it('should validate at branch level', () => {
+ expect(validateSetting({ days: '' })).toEqual({ isChanged: false, isValid: false });
+ expect(
+ validateSetting({
+ currentSetting: 'PREVIOUS_VERSION',
+ days: '12',
+ selected: 'NUMBER_OF_DAYS'
+ })
+ ).toEqual({ isChanged: true, isValid: true });
+ expect(
+ validateSetting({
+ currentSetting: 'PREVIOUS_VERSION',
+ days: 'nope',
+ selected: 'NUMBER_OF_DAYS'
+ })
+ ).toEqual({ isChanged: true, isValid: false });
+ expect(
+ validateSetting({
+ currentSetting: 'NUMBER_OF_DAYS',
+ currentSettingValue: '15',
+ days: '15',
+ selected: 'NUMBER_OF_DAYS'
+ })
+ ).toEqual({ isChanged: false, isValid: true });
+ expect(
+ validateSetting({
+ currentSetting: 'NUMBER_OF_DAYS',
+ currentSettingValue: '15',
+ days: '13',
+ selected: 'NUMBER_OF_DAYS'
+ })
+ ).toEqual({ isChanged: true, isValid: true });
+ expect(
+ validateSetting({
+ analysis: 'analysis1',
+ currentSetting: 'SPECIFIC_ANALYSIS',
+ currentSettingValue: 'analysis1',
+ days: '',
+ selected: 'SPECIFIC_ANALYSIS'
+ })
+ ).toEqual({ isChanged: false, isValid: true });
+ expect(
+ validateSetting({
+ analysis: 'analysis2',
+ currentSetting: 'SPECIFIC_ANALYSIS',
+ currentSettingValue: 'analysis1',
+ days: '',
+ selected: 'SPECIFIC_ANALYSIS'
+ })
+ ).toEqual({ isChanged: true, isValid: true });
+ });
+
+ it('should validate at project level', () => {
+ expect(validateSetting({ days: '', overrideGeneralSetting: false })).toEqual({
+ isChanged: false,
+ isValid: true
+ });
+ expect(validateSetting({ days: '', overrideGeneralSetting: true })).toEqual({
+ isChanged: true,
+ isValid: false
+ });
+ expect(
+ validateSetting({
+ currentSetting: 'PREVIOUS_VERSION',
+ days: '',
+ overrideGeneralSetting: false
+ })
+ ).toEqual({
+ isChanged: true,
+ isValid: true
+ });
+ });
+});
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 816c0805985..1c6ab579617 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
@@ -20,9 +20,8 @@
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
-import { Button } from 'sonar-ui-common/components/controls/buttons';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import { translate } from 'sonar-ui-common/helpers/l10n';
import { getNewCodePeriod, resetNewCodePeriod, setNewCodePeriod } from '../../../api/newCodePeriod';
import '../styles.css';
import { getSettingValue } from '../utils';
@@ -43,6 +42,7 @@ interface State {
days: string;
generalSetting?: T.NewCodePeriod;
loading: boolean;
+ overrideGeneralSetting?: boolean;
saving: boolean;
selected?: T.NewCodePeriodSettingType;
}
@@ -75,13 +75,17 @@ export default class App extends React.PureComponent<Props, State> {
}) {
const { currentSetting, currentSettingValue, generalSetting } = params;
+ const defaultDays =
+ (!currentSetting && generalSetting.type === 'NUMBER_OF_DAYS' && generalSetting.value) || '30';
+
return {
loading: false,
currentSetting,
currentSettingValue,
generalSetting,
- selected: currentSetting,
- days: currentSetting === 'NUMBER_OF_DAYS' ? currentSettingValue || '30' : '',
+ selected: currentSetting || generalSetting.type,
+ overrideGeneralSetting: Boolean(currentSetting),
+ days: (currentSetting === 'NUMBER_OF_DAYS' && currentSettingValue) || defaultDays,
analysis: (currentSetting === 'SPECIFIC_ANALYSIS' && currentSettingValue) || ''
};
}
@@ -92,7 +96,7 @@ export default class App extends React.PureComponent<Props, State> {
Promise.all([
getNewCodePeriod(),
getNewCodePeriod({
- branch: this.props.branchesEnabled ? 'master' : undefined,
+ branch: !this.props.branchesEnabled ? 'master' : undefined,
project: this.props.component.key
})
]).then(
@@ -137,11 +141,19 @@ export default class App extends React.PureComponent<Props, State> {
handleSelectSetting = (selected?: T.NewCodePeriodSettingType) => this.setState({ selected });
+ handleToggleSpecificSetting = (overrideGeneralSetting: boolean) =>
+ this.setState({ overrideGeneralSetting });
+
handleSubmit = (e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
const { component } = this.props;
- const { analysis, days, selected: type } = this.state;
+ const { analysis, days, selected: type, overrideGeneralSetting } = this.state;
+
+ if (!overrideGeneralSetting) {
+ this.resetSetting();
+ return;
+ }
const value = getSettingValue({ type, analysis, days });
@@ -201,17 +213,6 @@ export default class App extends React.PureComponent<Props, State> {
);
}
- renderGeneralSetting(generalSetting: T.NewCodePeriod) {
- if (generalSetting.type === 'NUMBER_OF_DAYS') {
- return `${translate('baseline.number_days')} (${translateWithParameters(
- 'duration.days',
- generalSetting.value || '?'
- )})`;
- } else {
- return translate('baseline.previous_version');
- }
- }
-
render() {
const { branchLikes, branchesEnabled, component } = this.props;
const {
@@ -221,6 +222,7 @@ export default class App extends React.PureComponent<Props, State> {
generalSetting,
loading,
currentSettingValue,
+ overrideGeneralSetting,
saving,
selected
} = this.state;
@@ -231,57 +233,45 @@ export default class App extends React.PureComponent<Props, State> {
{loading ? (
<DeferredSpinner />
) : (
- <div className="panel panel-white">
- {branchesEnabled && (
- <>
- <h2>{translate('project_baseline.default_setting')}</h2>
- <p>{translate('project_baseline.default_setting.description')}</p>
- </>
- )}
+ <div className="panel-white project-baseline">
+ {branchesEnabled && <h2>{translate('project_baseline.default_setting')}</h2>}
- {generalSetting && (
- <div className="text-right spacer-bottom">
- {currentSetting && (
- <>
- <Button className="little-spacer-bottom" onClick={this.resetSetting}>
- {translate('project_baseline.reset_to_general')}
- </Button>
- </>
- )}
- <div className="spacer-top medium">
- <strong>{translate('project_baseline.general_setting')}: </strong>
- {this.renderGeneralSetting(generalSetting)}
- </div>
- </div>
+ {generalSetting && overrideGeneralSetting !== undefined && (
+ <ProjectBaselineSelector
+ analysis={analysis}
+ branchesEnabled={branchesEnabled}
+ component={component.key}
+ currentSetting={currentSetting}
+ currentSettingValue={currentSettingValue}
+ days={days}
+ generalSetting={generalSetting}
+ onSelectAnalysis={this.handleSelectAnalysis}
+ onSelectDays={this.handleSelectDays}
+ onSelectSetting={this.handleSelectSetting}
+ onSubmit={this.handleSubmit}
+ onToggleSpecificSetting={this.handleToggleSpecificSetting}
+ overrideGeneralSetting={overrideGeneralSetting}
+ saving={saving}
+ selected={selected}
+ />
)}
-
- <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 && branchesEnabled && (
- <BranchList
- branchLikes={branchLikes}
- component={component}
- inheritedSetting={
- currentSetting
- ? {
- type: currentSetting,
- value: currentSettingValue
- }
- : generalSetting
- }
- />
+ <div className="huge-spacer-top branch-baseline-selector">
+ <hr />
+ <h2>{translate('project_baseline.configure_branches')}</h2>
+ <BranchList
+ branchLikes={branchLikes}
+ component={component}
+ inheritedSetting={
+ currentSetting
+ ? {
+ type: currentSetting,
+ value: currentSettingValue
+ }
+ : generalSetting
+ }
+ />
+ </div>
)}
</div>
)}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx
index 9cb63f366af..f56491c551c 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx
@@ -22,13 +22,15 @@ import RadioCard from 'sonar-ui-common/components/controls/RadioCard';
import { translate } from 'sonar-ui-common/helpers/l10n';
export interface Props {
+ disabled?: boolean;
onSelect: (selection: T.NewCodePeriodSettingType) => void;
selected: boolean;
}
-export default function BaselineSettingAnalysis({ onSelect, selected }: Props) {
+export default function BaselineSettingAnalysis({ disabled, onSelect, selected }: Props) {
return (
<RadioCard
+ disabled={disabled}
onClick={() => onSelect('SPECIFIC_ANALYSIS')}
selected={selected}
title={translate('baseline.specific_analysis')}>
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx
index 3e8b505283d..a2d859bafe0 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx
@@ -25,6 +25,7 @@ import { translate } from 'sonar-ui-common/helpers/l10n';
export interface Props {
className?: string;
days: string;
+ disabled?: boolean;
isChanged: boolean;
isValid: boolean;
onChangeDays: (value: string) => void;
@@ -33,10 +34,11 @@ export interface Props {
}
export default function BaselineSettingDays(props: Props) {
- const { className, days, isChanged, isValid, onChangeDays, onSelect, selected } = props;
+ const { className, days, disabled, isChanged, isValid, onChangeDays, onSelect, selected } = props;
return (
<RadioCard
className={className}
+ disabled={disabled}
onClick={() => onSelect('NUMBER_OF_DAYS')}
selected={selected}
title={translate('baseline.number_days')}>
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingPreviousVersion.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingPreviousVersion.tsx
index aae1d40a52b..a75051c4d0d 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingPreviousVersion.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingPreviousVersion.tsx
@@ -22,14 +22,17 @@ import RadioCard from 'sonar-ui-common/components/controls/RadioCard';
import { translate } from 'sonar-ui-common/helpers/l10n';
export interface Props {
+ disabled?: boolean;
isDefault?: boolean;
onSelect: (selection: T.NewCodePeriodSettingType) => void;
selected: boolean;
}
-export default function BaselineSettingPreviousVersion({ isDefault, onSelect, selected }: Props) {
+export default function BaselineSettingPreviousVersion(props: Props) {
+ const { disabled, isDefault, onSelect, selected } = props;
return (
<RadioCard
+ disabled={disabled}
onClick={() => onSelect('PREVIOUS_VERSION')}
selected={selected}
title={
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 b2ed30fd434..04bb1688782 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
@@ -26,6 +26,7 @@ import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { parseDate, toShortNotSoISOString } from 'sonar-ui-common/helpers/dates';
import { translate } from 'sonar-ui-common/helpers/l10n';
+import { scrollToElement } from 'sonar-ui-common/helpers/scrolling';
import { getProjectActivity } from '../../../api/projectActivity';
import DateFormatter from '../../../components/intl/DateFormatter';
import TimeFormatter from '../../../components/intl/TimeFormatter';
@@ -49,6 +50,7 @@ interface State {
export default class BranchAnalysisList extends React.PureComponent<Props, State> {
mounted = false;
badges: T.Dict<HTMLDivElement> = {};
+ rootNodeRef: React.RefObject<HTMLDivElement>;
state: State = {
analyses: [],
loading: true,
@@ -58,6 +60,7 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
constructor(props: Props) {
super(props);
+ this.rootNodeRef = React.createRef<HTMLDivElement>();
this.updateScroll = throttle(this.updateScroll, 20);
}
@@ -70,6 +73,13 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
this.mounted = false;
}
+ scrollToSelected() {
+ const selectedNode = document.querySelector('.branch-analysis.selected');
+ if (this.rootNodeRef.current && selectedNode) {
+ scrollToElement(selectedNode, { parent: this.rootNodeRef.current, bottomOffset: 40 });
+ }
+ }
+
fetchAnalyses(initial = false) {
const { analysis, branch, component } = this.props;
const { range } = this.state;
@@ -86,13 +96,18 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
return;
}
- this.setState({
- analyses: result.analyses.map(analysis => ({
- ...analysis,
- date: parseDate(analysis.date)
- })) as T.ParsedAnalysis[],
- loading: false
- });
+ this.setState(
+ {
+ analyses: result.analyses.map(analysis => ({
+ ...analysis,
+ date: parseDate(analysis.date)
+ })) as T.ParsedAnalysis[],
+ loading: false
+ },
+ () => {
+ this.scrollToSelected();
+ }
+ );
});
}
@@ -115,11 +130,9 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
}
};
- shouldStick = (version: string, index: number) => {
+ shouldStick = (version: string) => {
const badge = this.badges[version];
- return (
- badge && Number(badge.getAttribute('originOffsetTop')) < this.state.scroll + 18 + index * 2
- );
+ return badge && Number(badge.getAttribute('originOffsetTop')) < this.state.scroll + 10;
};
getRangeOptions() {
@@ -168,7 +181,10 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
/>
</div>
<div className="branch-analysis-list-wrapper">
- <div className="bordered branch-analysis-list" onScroll={this.handleScroll}>
+ <div
+ className="bordered branch-analysis-list"
+ onScroll={this.handleScroll}
+ ref={this.rootNodeRef}>
{loading && <DeferredSpinner className="big-spacer-top" />}
{!loading && !hasFilteredData ? (
@@ -188,7 +204,7 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
<div
className={classNames('branch-analysis-version-badge', {
first: idx === 0,
- sticky: this.shouldStick(version.version, idx)
+ sticky: this.shouldStick(version.version)
})}
ref={this.registerBadgeNode(version.version)}>
<Tooltip
@@ -212,7 +228,7 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
version.byDay[day].map(analysis => (
<li
className={classNames('branch-analysis', {
- selected: false
+ selected: analysis.key === this.props.analysis
})}
data-date={parseDate(analysis.date).valueOf()}
key={analysis.key}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx
index 234cac81b58..95ad1db5475 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx
@@ -168,7 +168,6 @@ export default class BranchList extends React.PureComponent<Props, State> {
<thead>
<tr>
<th>{translate('branch_list.branch')}</th>
- <th className="thin"> </th>
<th className="thin nowrap huge-spacer-right">
{translate('branch_list.current_setting')}
</th>
@@ -185,15 +184,10 @@ export default class BranchList extends React.PureComponent<Props, State> {
<div className="badge spacer-left">{translate('branches.main_branch')}</div>
)}
</td>
- <td>
- {!branch.newCodePeriod && (
- <span className="badge badge-info">{translate('default')}</span>
- )}
- </td>
<td className="huge-spacer-right nowrap">
{branch.newCodePeriod
? this.renderNewCodePeriodSetting(branch.newCodePeriod)
- : this.renderNewCodePeriodSetting(this.props.inheritedSetting)}
+ : translate('branch_list.default_setting')}
</td>
<td className="text-right">
<ActionsDropdown>
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 5aabd1ad5ac..e587ae19b25 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
@@ -20,8 +20,9 @@
import * as classNames from 'classnames';
import * as React from 'react';
import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
+import Radio from 'sonar-ui-common/components/controls/Radio';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
-import { translate } from 'sonar-ui-common/helpers/l10n';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { validateSetting } from '../utils';
import BaselineSettingAnalysis from './BaselineSettingAnalysis';
import BaselineSettingDays from './BaselineSettingDays';
@@ -35,12 +36,36 @@ export interface ProjectBaselineSelectorProps {
currentSetting?: T.NewCodePeriodSettingType;
currentSettingValue?: string;
days: string;
+ generalSetting: T.NewCodePeriod;
onSelectAnalysis: (analysis: T.ParsedAnalysis) => void;
onSelectDays: (value: string) => void;
- onSelectSetting: (value: T.NewCodePeriodSettingType) => void;
+ onSelectSetting: (value?: T.NewCodePeriodSettingType) => void;
onSubmit: (e: React.SyntheticEvent<HTMLFormElement>) => void;
+ onToggleSpecificSetting: (selection: boolean) => void;
saving: boolean;
selected?: T.NewCodePeriodSettingType;
+ overrideGeneralSetting: boolean;
+}
+
+function renderGeneralSetting(generalSetting: T.NewCodePeriod) {
+ let setting: string;
+ let description: string;
+ if (generalSetting.type === 'NUMBER_OF_DAYS') {
+ setting = `${translate('baseline.number_days')} (${translateWithParameters(
+ 'duration.days',
+ generalSetting.value || '?'
+ )})`;
+ description = translate('baseline.number_days.description');
+ } else {
+ setting = translate('baseline.previous_version');
+ description = translate('baseline.previous_version.description');
+ }
+
+ return (
+ <div className="general-setting">
+ <strong>{setting}</strong>: {description}
+ </div>
+ );
}
export default function ProjectBaselineSelector(props: ProjectBaselineSelectorProps) {
@@ -49,10 +74,12 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr
branchesEnabled,
component,
currentSetting,
- days,
currentSettingValue,
+ days,
+ generalSetting,
saving,
- selected
+ selected,
+ overrideGeneralSetting
} = props;
const { isChanged, isValid } = validateSetting({
@@ -60,29 +87,52 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr
currentSetting,
currentSettingValue,
days,
- selected
+ selected,
+ overrideGeneralSetting
});
return (
<form className="project-baseline-selector" onSubmit={props.onSubmit}>
- <div className="branch-baseline-setting-modal">
+ <div className="big-spacer-top spacer-bottom" role="radiogroup">
+ <Radio
+ checked={!overrideGeneralSetting}
+ className="big-spacer-bottom"
+ onCheck={() => props.onToggleSpecificSetting(false)}
+ value="general">
+ {translate('project_baseline.general_setting')}
+ </Radio>
+ <div className="big-spacer-left">{renderGeneralSetting(generalSetting)}</div>
+
+ <Radio
+ checked={overrideGeneralSetting}
+ className="huge-spacer-top"
+ onCheck={() => props.onToggleSpecificSetting(true)}
+ value="specific">
+ {translate('project_baseline.specific_setting')}
+ </Radio>
+ </div>
+
+ <div className="big-spacer-left big-spacer-right branch-baseline-setting-modal">
<div className="display-flex-row big-spacer-bottom" role="radiogroup">
<BaselineSettingPreviousVersion
+ disabled={!overrideGeneralSetting}
onSelect={props.onSelectSetting}
- selected={selected === 'PREVIOUS_VERSION'}
+ selected={overrideGeneralSetting && selected === 'PREVIOUS_VERSION'}
/>
<BaselineSettingDays
days={days}
+ disabled={!overrideGeneralSetting}
isChanged={isChanged}
isValid={isValid}
onChangeDays={props.onSelectDays}
onSelect={props.onSelectSetting}
- selected={selected === 'NUMBER_OF_DAYS'}
+ selected={overrideGeneralSetting && selected === 'NUMBER_OF_DAYS'}
/>
{!branchesEnabled && (
<BaselineSettingAnalysis
+ disabled={!overrideGeneralSetting}
onSelect={props.onSelectSetting}
- selected={selected === 'SPECIFIC_ANALYSIS'}
+ selected={overrideGeneralSetting && selected === 'SPECIFIC_ANALYSIS'}
/>
)}
</div>
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 9bffdfa35cc..3a23b727efc 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css
@@ -17,12 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+.project-baseline {
+ padding: calc(4 * var(--gridSize));
+}
.project-baseline-selector > .branch-baseline-setting-modal {
max-height: 60vh;
padding-top: 2px;
}
+.project-baseline-selector .general-setting {
+ margin-left: 7px;
+}
+
+.branch-baseline-selector > hr {
+ margin: 0 calc(-4 * var(--gridSize)) calc(4 * var(--gridSize));
+}
+
.branch-baseline-setting-modal {
display: flex;
flex-direction: column;
@@ -44,7 +55,7 @@
}
.branch-analysis-list > ul {
- padding-top: 52px;
+ padding-top: 18px;
}
.branch-analysis-date {
@@ -90,6 +101,10 @@
background-color: white;
}
+.branch-analysis-version-badge.sticky + .branch-analysis-days-list {
+ padding-top: 36px;
+}
+
.branch-analysis-version-badge.sticky,
.branch-analysis-version-badge.first {
position: absolute;
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 69b7cfabda0..99ee11e50dd 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts
@@ -48,15 +48,30 @@ export function validateSetting(state: {
currentSettingValue?: string;
days: string;
selected?: T.NewCodePeriodSettingType;
+ overrideGeneralSetting?: boolean;
}) {
- const { analysis = '', currentSetting, currentSettingValue, days, selected } = state;
+ const {
+ analysis = '',
+ currentSetting,
+ currentSettingValue,
+ days,
+ selected,
+ overrideGeneralSetting
+ } = state;
- const isChanged =
- selected !== currentSetting ||
- (selected === 'NUMBER_OF_DAYS' && days !== currentSettingValue) ||
- (selected === 'SPECIFIC_ANALYSIS' && analysis !== currentSettingValue);
+ let isChanged;
+ if (!currentSetting && overrideGeneralSetting !== undefined) {
+ isChanged = overrideGeneralSetting;
+ } else {
+ isChanged =
+ overrideGeneralSetting === false ||
+ selected !== currentSetting ||
+ (selected === 'NUMBER_OF_DAYS' && days !== currentSettingValue) ||
+ (selected === 'SPECIFIC_ANALYSIS' && analysis !== currentSettingValue);
+ }
const isValid =
+ overrideGeneralSetting === false ||
selected === 'PREVIOUS_VERSION' ||
(selected === 'SPECIFIC_ANALYSIS' && analysis.length > 0) ||
(selected === 'NUMBER_OF_DAYS' && validateDays(days));
diff --git a/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx b/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx
index 82f282f8701..1b7d8e7230e 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
+import { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
@@ -84,6 +84,13 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> {
this.setState({ selected, success: false });
};
+ onCancel = () => {
+ this.setState(({ currentSetting, currentSettingValue, days }) => ({
+ selected: currentSetting,
+ days: currentSetting === 'NUMBER_OF_DAYS' ? String(currentSettingValue) : days
+ }));
+ };
+
onSubmit = (e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
@@ -191,6 +198,9 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> {
<SubmitButton disabled={saving || !isValid}>
{translate('save')}
</SubmitButton>
+ <ResetButtonLink className="spacer-left" onClick={this.onCancel}>
+ {translate('cancel')}
+ </ResetButtonLink>
</div>
)}
{!saving && !loading && success && (
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts
index 917b797d311..53689b77ce4 100644
--- a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts
+++ b/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts
@@ -85,15 +85,12 @@ describe('getPeriodLabel', () => {
it('should handle SPECIFIC_ANALYSIS', () => {
expect(
- getPeriodLabel(
- mockPeriod({
- mode: 'SPECIFIC_ANALYSIS',
- parameter: 'should be overriden'
- }),
- formatter
- )
- ).toBe('overview.period.specific_analysis.2019-04-23T02:12:32+0100');
- expect(formatter).toBeCalled();
+ getPeriodLabel(mockPeriod({ mode: 'SPECIFIC_ANALYSIS', modeParam: 'A658678DE' }), formatter)
+ ).toBe('overview.period.specific_analysis.A658678DE');
+ expect(getPeriodLabel(mockPeriod({ mode: 'SPECIFIC_ANALYSIS' }), formatter)).toBe(
+ 'overview.period.specific_analysis.2019-04-23T02:12:32+0100'
+ );
+ expect(formatter).toBeCalledTimes(1);
});
it('should handle PREVIOUS_VERSION', () => {
diff --git a/server/sonar-web/src/main/js/helpers/periods.ts b/server/sonar-web/src/main/js/helpers/periods.ts
index 6a9702fec7b..b2442175b78 100644
--- a/server/sonar-web/src/main/js/helpers/periods.ts
+++ b/server/sonar-web/src/main/js/helpers/periods.ts
@@ -43,7 +43,7 @@ export function getPeriodLabel(
switch (period.mode) {
case 'SPECIFIC_ANALYSIS':
- parameter = dateFormatter(period.date);
+ parameter = parameter || dateFormatter(period.date);
break;
case 'PREVIOUS_VERSION':
parameter = parameter || dateFormatter(period.date);