aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2019-08-28 11:42:06 +0200
committerSonarTech <sonartech@sonarsource.com>2019-09-24 20:21:16 +0200
commit0cb53fe7d87b78dab98775ac94d9da87b561ce99 (patch)
treee3b67b6a61f7a08a4e96d80f65c2e6676e5c5811 /server/sonar-web
parent80f844f576932e2d457ce5d0741f790e92fc50ba (diff)
downloadsonarqube-0cb53fe7d87b78dab98775ac94d9da87b561ce99.tar.gz
sonarqube-0cb53fe7d87b78dab98775ac94d9da87b561ce99.zip
SONAR-11629 Add baseline marker to activity page
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/api/newCodePeriod.ts2
-rw-r--r--server/sonar-web/src/main/js/app/types.d.ts14
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx59
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx32
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysis-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysis-test.tsx.snap59
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css38
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchList-test.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap16
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx2
23 files changed, 275 insertions, 99 deletions
diff --git a/server/sonar-web/src/main/js/api/newCodePeriod.ts b/server/sonar-web/src/main/js/api/newCodePeriod.ts
index 8043702a12b..1c21cefa735 100644
--- a/server/sonar-web/src/main/js/api/newCodePeriod.ts
+++ b/server/sonar-web/src/main/js/api/newCodePeriod.ts
@@ -31,7 +31,7 @@ export function setNewCodePeriod(data: {
project?: string;
branch?: string;
type: T.NewCodePeriodSettingType;
- value: string | null;
+ value?: string;
}): Promise<void> {
return post('/api/new_code_periods/set', 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 23a911a076b..c5ccff569da 100644
--- a/server/sonar-web/src/main/js/app/types.d.ts
+++ b/server/sonar-web/src/main/js/app/types.d.ts
@@ -115,10 +115,7 @@ declare namespace T {
export type BranchType = 'LONG' | 'SHORT';
export interface BranchWithNewCodePeriod extends Branch {
- newCodePeriod?: {
- type: NewCodePeriodSettingType;
- value: string | null;
- };
+ newCodePeriod?: NewCodePeriod;
}
export interface Breadcrumb {
@@ -506,12 +503,19 @@ declare namespace T {
qualityGate?: string;
}
+ export interface NewCodePeriod {
+ type?: NewCodePeriodSettingType;
+ value?: string;
+ effectiveValue?: string;
+ }
+
export interface NewCodePeriodBranch {
projectKey: string;
branchKey: string;
inherited?: boolean;
type?: NewCodePeriodSettingType;
- value?: string | null;
+ value?: string;
+ effectiveValue?: string;
}
export type NewCodePeriodSettingType =
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 19a8c041be4..d6de9e60612 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
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as classNames from 'classnames';
+import { isEqual } from 'date-fns';
import { throttle } from 'lodash';
import * as React from 'react';
import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
@@ -45,6 +46,7 @@ interface Props {
deleteAnalysis: (analysis: string) => Promise<void>;
deleteEvent: (analysis: string, event: string) => Promise<void>;
initializing: boolean;
+ leakPeriodDate?: Date;
project: { qualifier: string };
query: Query;
updateQuery: (changes: Partial<Query>) => void;
@@ -155,6 +157,40 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
this.props.updateQuery({ selectedDate: date });
};
+ shouldRenderBaselineMarker(analysis: ParsedAnalysis): boolean {
+ return Boolean(
+ analysis.manualNewCodePeriodBaseline ||
+ (this.props.leakPeriodDate && isEqual(this.props.leakPeriodDate, analysis.date))
+ );
+ }
+
+ renderAnalysis(analysis: ParsedAnalysis) {
+ const firstAnalysisKey = this.props.analyses[0].key;
+
+ const selectedDate = this.props.query.selectedDate
+ ? this.props.query.selectedDate.valueOf()
+ : null;
+
+ return (
+ <ProjectActivityAnalysis
+ addCustomEvent={this.props.addCustomEvent}
+ addVersion={this.props.addVersion}
+ analysis={analysis}
+ canAdmin={this.props.canAdmin}
+ canCreateVersion={this.props.project.qualifier === 'TRK'}
+ canDeleteAnalyses={this.props.canDeleteAnalyses}
+ changeEvent={this.props.changeEvent}
+ deleteAnalysis={this.props.deleteAnalysis}
+ deleteEvent={this.props.deleteEvent}
+ isBaseline={this.shouldRenderBaselineMarker(analysis)}
+ isFirst={analysis.key === firstAnalysisKey}
+ key={analysis.key}
+ selected={analysis.date.valueOf() === selectedDate}
+ updateSelectedDate={this.updateSelectedDate}
+ />
+ );
+ }
+
render() {
const byVersionByDay = getAnalysesByVersionByDay(this.props.analyses, this.props.query);
const hasFilteredData =
@@ -174,11 +210,6 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
);
}
- const firstAnalysisKey = this.props.analyses[0].key;
- const selectedDate = this.props.query.selectedDate
- ? this.props.query.selectedDate.valueOf()
- : null;
-
return (
<ul
className={classNames('project-activity-versions-list', this.props.className)}
@@ -212,23 +243,7 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
</div>
<ul className="project-activity-analyses-list">
{version.byDay[day] != null &&
- version.byDay[day].map(analysis => (
- <ProjectActivityAnalysis
- addCustomEvent={this.props.addCustomEvent}
- addVersion={this.props.addVersion}
- analysis={analysis}
- canAdmin={this.props.canAdmin}
- canCreateVersion={this.props.project.qualifier === 'TRK'}
- canDeleteAnalyses={this.props.canDeleteAnalyses}
- changeEvent={this.props.changeEvent}
- deleteAnalysis={this.props.deleteAnalysis}
- deleteEvent={this.props.deleteEvent}
- isFirst={analysis.key === firstAnalysisKey}
- key={analysis.key}
- selected={analysis.date.valueOf() === selectedDate}
- updateSelectedDate={this.updateSelectedDate}
- />
- ))}
+ version.byDay[day].map(analysis => this.renderAnalysis(analysis))}
</ul>
</li>
))}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
index e24bd6e9f87..515388f74d8 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
@@ -23,6 +23,7 @@ import ActionsDropdown, {
ActionsDropdownDivider,
ActionsDropdownItem
} from 'sonar-ui-common/components/controls/ActionsDropdown';
+import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import { parseDate } from 'sonar-ui-common/helpers/dates';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
@@ -42,6 +43,7 @@ interface Props {
changeEvent: (event: string, name: string) => Promise<void>;
deleteAnalysis: (analysis: string) => Promise<void>;
deleteEvent: (analysis: string, event: string) => Promise<void>;
+ isBaseline: boolean;
isFirst: boolean;
selected: boolean;
updateSelectedDate: (date: Date) => void;
@@ -51,7 +53,6 @@ interface State {
addEventForm: boolean;
addVersionForm: boolean;
removeAnalysisForm: boolean;
- suppressVersionTooltip?: boolean;
}
export default class ProjectActivityAnalysis extends React.PureComponent<Props, State> {
@@ -102,22 +103,31 @@ export default class ProjectActivityAnalysis extends React.PureComponent<Props,
this.setState({ addVersionForm: true });
};
- handleTimeTooltipHide = () => {
- this.setState({ suppressVersionTooltip: false });
- };
-
- handleTimeTooltipShow = () => {
- this.setState({ suppressVersionTooltip: true });
- };
-
closeAddVersionForm = () => {
if (this.mounted) {
this.setState({ addVersionForm: false });
}
};
+ renderBaselineMarker() {
+ return (
+ <div className="baseline-marker">
+ <div className="wedge" />
+ <hr />
+ <div className="label display-flex-center">
+ {translate('project_activity.new_code_period_start')}
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay={translate('project_activity.new_code_period_start.help')}
+ placement="top"
+ />
+ </div>
+ </div>
+ );
+ }
+
render() {
- const { analysis, isFirst, canAdmin, canCreateVersion } = this.props;
+ const { analysis, isBaseline, isFirst, canAdmin, canCreateVersion } = this.props;
const { date, events } = analysis;
const parsedDate = parseDate(date);
const hasVersion = events.find(event => event.category === 'VERSION') != null;
@@ -222,6 +232,8 @@ export default class ProjectActivityAnalysis extends React.PureComponent<Props,
isFirst={this.props.isFirst}
/>
)}
+
+ {isBaseline && this.renderBaselineMarker()}
</li>
</Tooltip>
);
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 f5ad361bd9a..8766d537f91 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
@@ -82,6 +82,9 @@ export default function ProjectActivityApp(props: Props) {
deleteAnalysis={props.deleteAnalysis}
deleteEvent={props.deleteEvent}
initializing={props.initializing}
+ leakPeriodDate={
+ props.project.leakPeriodDate ? parseDate(props.project.leakPeriodDate) : undefined
+ }
project={props.project}
query={props.query}
updateQuery={props.updateQuery}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.tsx
index 3f4e5a879ed..4a40545a1e4 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.tsx
@@ -34,10 +34,12 @@ jest.mock('sonar-ui-common/helpers/dates', () => {
return { ...actual, toShortNotSoISOString: (date: string) => 'ISO.' + date };
});
+const DATE = parseDate('2016-10-27T16:33:50+0000');
+
const ANALYSES = [
{
key: 'A1',
- date: parseDate('2016-10-27T16:33:50+0000'),
+ date: DATE,
events: [{ key: 'E1', category: 'VERSION', name: '6.5-SNAPSHOT' }]
},
{ key: 'A2', date: parseDate('2016-10-27T12:21:15+0000'), events: [] },
@@ -66,6 +68,7 @@ const DEFAULT_PROPS: ProjectActivityAnalysesList['props'] = {
deleteAnalysis: jest.fn().mockResolvedValue(undefined),
deleteEvent: jest.fn().mockResolvedValue(undefined),
initializing: false,
+ leakPeriodDate: parseDate('2016-10-27T12:21:15+0000'),
project: { qualifier: 'TRK' },
query: {
category: '',
@@ -91,8 +94,8 @@ it('should correctly filter analyses by date range', () => {
wrapper.setProps({
query: {
...DEFAULT_PROPS.query,
- from: parseDate('2016-10-27T16:33:50+0000'),
- to: parseDate('2016-10-27T16:33:50+0000')
+ from: DATE,
+ to: DATE
}
});
expect(wrapper).toMatchSnapshot();
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysis-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysis-test.tsx
index 8e842ab107c..175ea87e343 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysis-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysis-test.tsx
@@ -38,6 +38,7 @@ it('should render correctly', () => {
expect(
shallowRender({ analysis: mockParsedAnalysis({ buildString: '1.0.234' }) })
).toMatchSnapshot();
+ expect(shallowRender({ isBaseline: true })).toMatchSnapshot();
});
it('should show the correct admin options', () => {
@@ -87,6 +88,7 @@ function shallowRender(props: Partial<ProjectActivityAnalysis['props']> = {}) {
changeEvent={jest.fn()}
deleteAnalysis={jest.fn()}
deleteEvent={jest.fn()}
+ isBaseline={false}
isFirst={false}
selected={false}
updateSelectedDate={jest.fn()}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.tsx.snap
index 801e98e8a31..37ab95a8e9e 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.tsx.snap
@@ -67,6 +67,7 @@ exports[`should correctly filter analyses by category 1`] = `
changeEvent={[MockFunction]}
deleteAnalysis={[MockFunction]}
deleteEvent={[MockFunction]}
+ isBaseline={false}
isFirst={false}
key="A4"
selected={false}
@@ -146,6 +147,7 @@ exports[`should correctly filter analyses by date range 1`] = `
changeEvent={[MockFunction]}
deleteAnalysis={[MockFunction]}
deleteEvent={[MockFunction]}
+ isBaseline={false}
isFirst={true}
key="A1"
selected={false}
@@ -225,6 +227,7 @@ exports[`should render correctly 1`] = `
changeEvent={[MockFunction]}
deleteAnalysis={[MockFunction]}
deleteEvent={[MockFunction]}
+ isBaseline={false}
isFirst={true}
key="A1"
selected={false}
@@ -245,6 +248,7 @@ exports[`should render correctly 1`] = `
changeEvent={[MockFunction]}
deleteAnalysis={[MockFunction]}
deleteEvent={[MockFunction]}
+ isBaseline={true}
isFirst={false}
key="A2"
selected={false}
@@ -316,6 +320,7 @@ exports[`should render correctly 1`] = `
changeEvent={[MockFunction]}
deleteAnalysis={[MockFunction]}
deleteEvent={[MockFunction]}
+ isBaseline={false}
isFirst={false}
key="A3"
selected={false}
@@ -360,6 +365,7 @@ exports[`should render correctly 1`] = `
changeEvent={[MockFunction]}
deleteAnalysis={[MockFunction]}
deleteEvent={[MockFunction]}
+ isBaseline={false}
isFirst={false}
key="A4"
selected={false}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysis-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysis-test.tsx.snap
index 722f614714e..6e8e9a74b2a 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysis-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysis-test.tsx.snap
@@ -221,6 +221,65 @@ exports[`should render correctly 3`] = `
</Tooltip>
`;
+exports[`should render correctly 4`] = `
+<Tooltip
+ mouseEnterDelay={0.5}
+ overlay={
+ <TimeFormatter
+ date={
+ Object {
+ "toISOString": [Function],
+ "valueOf": [Function],
+ }
+ }
+ long={true}
+ />
+ }
+ placement="left"
+>
+ <li
+ className="project-activity-analysis"
+ data-date={1546333200000}
+ onClick={[Function]}
+ tabIndex={0}
+ >
+ <div
+ className="project-activity-time spacer-right"
+ >
+ <TimeFormatter
+ date={
+ Object {
+ "toISOString": [Function],
+ "valueOf": [Function],
+ }
+ }
+ long={false}
+ >
+ <Component />
+ </TimeFormatter>
+ </div>
+ <div
+ className="baseline-marker"
+ >
+ <div
+ className="wedge"
+ />
+ <hr />
+ <div
+ className="label display-flex-center"
+ >
+ project_activity.new_code_period_start
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay="project_activity.new_code_period_start.help"
+ placement="top"
+ />
+ </div>
+ </div>
+ </li>
+</Tooltip>
+`;
+
exports[`should show the correct admin options 1`] = `
<Tooltip
mouseEnterDelay={0.5}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap
index 324678b7dc9..635b6c84a4c 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap
@@ -80,6 +80,7 @@ exports[`should render correctly 1`] = `
deleteAnalysis={[MockFunction]}
deleteEvent={[MockFunction]}
initializing={false}
+ leakPeriodDate={2017-05-16T11:50:02.000Z}
project={
Object {
"key": "foo",
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css
index acb32386265..b20002bd654 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css
@@ -61,9 +61,6 @@
flex-shrink: 0;
}
-.project-activity-days-list {
-}
-
.project-activity-day {
margin-top: 8px;
margin-bottom: 24px;
@@ -79,14 +76,11 @@
font-weight: bold;
}
-.project-activity-analyses-list {
-}
-
.project-activity-analysis {
position: relative;
display: flex;
min-height: var(--smallControlHeight);
- padding: calc(0.5 * var(--gridSize));
+ padding: var(--gridSize) calc(0.5 * var(--gridSize));
border-top: 1px solid var(--barBorderColor);
border-bottom: 1px solid var(--barBorderColor);
cursor: pointer;
@@ -294,3 +288,33 @@
.project-activity-graph-tooltip-value {
font-weight: bold;
}
+
+.baseline-marker {
+ position: absolute;
+ top: -10px;
+ left: 0;
+ right: 0;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.baseline-marker > .wedge {
+ border: 10px solid transparent;
+ border-left-color: var(--leakBorderColor);
+}
+
+.baseline-marker > hr {
+ border: none;
+ margin: 0 0 0 -11px;
+ background-color: var(--leakBorderColor);
+ height: 2px;
+ flex: 1 0 auto;
+}
+
+.baseline-marker > .label {
+ background-color: var(--leakColor);
+ border: 1px solid var(--leakBorderColor);
+ padding: 2px 8px;
+ font-size: var(--verySmallFontSize);
+}
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 a30c435de00..230a2ec74c9 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
@@ -104,7 +104,7 @@ describe('getSettingValue', () => {
it('should work for Previous version', () => {
wrapper.setState({ selected: 'PREVIOUS_VERSION' });
- expect(wrapper.instance().getSettingValue()).toBeNull();
+ expect(wrapper.instance().getSettingValue()).toBeUndefined();
});
});
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchList-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchList-test.tsx
index 73cff093697..0c34ed35b6b 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchList-test.tsx
@@ -102,16 +102,32 @@ it('should render the right setting label', () => {
expect(
wrapper.instance().renderNewCodePeriodSetting({ type: 'NUMBER_OF_DAYS', value: '21' })
).toBe('baseline.number_days: 21');
+ expect(wrapper.instance().renderNewCodePeriodSetting({ type: 'PREVIOUS_VERSION' })).toBe(
+ 'baseline.previous_version'
+ );
expect(
- wrapper.instance().renderNewCodePeriodSetting({ type: 'PREVIOUS_VERSION', value: null })
- ).toBe('baseline.previous_version');
- expect(
- wrapper.instance().renderNewCodePeriodSetting({ type: 'SPECIFIC_ANALYSIS', value: 'A85835' })
- ).toBe('baseline.specific_analysis: A85835');
+ wrapper.instance().renderNewCodePeriodSetting({
+ type: 'SPECIFIC_ANALYSIS',
+ value: 'A85835',
+ effectiveValue: '2018-12-02T13:01:12'
+ })
+ ).toMatchInlineSnapshot(`
+ <React.Fragment>
+ baseline.specific_analysis:
+ <DateTimeFormatter
+ date="2018-12-02T13:01:12"
+ />
+ </React.Fragment>
+ `);
});
function shallowRender(props: Partial<BranchList['props']> = {}) {
return shallow<BranchList>(
- <BranchList branchLikes={[]} component={mockComponent()} {...props} />
+ <BranchList
+ branchLikes={[]}
+ component={mockComponent()}
+ inheritedSetting={{ type: 'PREVIOUS_VERSION' }}
+ {...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 51582805875..571628b3fda 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,6 +11,11 @@ exports[`should render correctly 1`] = `
branch_list.branch
</th>
<th
+ className="thin"
+ >
+
+ </th>
+ <th
className="thin nowrap huge-spacer-right"
>
branch_list.current_setting
@@ -36,6 +41,7 @@ exports[`should render correctly 1`] = `
"isMain": true,
"name": "master",
"newCodePeriod": Object {
+ "effectiveValue": undefined,
"type": "NUMBER_OF_DAYS",
"value": "27",
},
@@ -50,6 +56,7 @@ exports[`should render correctly 1`] = `
branches.main_branch
</div>
</td>
+ <td />
<td
className="huge-spacer-right nowrap"
>
@@ -91,9 +98,7 @@ exports[`should render correctly 1`] = `
/>
branch-6.7
</td>
- <td
- className="huge-spacer-right nowrap"
- >
+ <td>
<span
className="badge badge-info"
>
@@ -101,6 +106,11 @@ exports[`should render correctly 1`] = `
</span>
</td>
<td
+ className="huge-spacer-right nowrap"
+ >
+ baseline.previous_version
+ </td>
+ <td
className="text-right"
>
<ActionsDropdown>
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 992ffd8fc20..c88e2fc9e64 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
@@ -36,9 +36,9 @@ interface Props {
interface State {
currentSetting?: T.NewCodePeriodSettingType;
- currentSettingValue?: string | number;
+ currentSettingValue?: string;
days: string;
- generalSetting?: { type: T.NewCodePeriodSettingType; value?: string };
+ generalSetting?: T.NewCodePeriod;
loading: boolean;
saving: boolean;
selected?: T.NewCodePeriodSettingType;
@@ -126,7 +126,7 @@ export default class App extends React.PureComponent<Props, State> {
const { days, selected } = this.state;
const type = selected;
- const value = type === 'NUMBER_OF_DAYS' ? days : null;
+ const value = type === 'NUMBER_OF_DAYS' ? days : undefined;
if (type) {
this.setState({ saving: true });
@@ -184,7 +184,7 @@ export default class App extends React.PureComponent<Props, State> {
);
}
- renderGeneralSetting(generalSetting: { type: T.NewCodePeriodSettingType; value?: string }) {
+ renderGeneralSetting(generalSetting: T.NewCodePeriod) {
if (generalSetting.type === 'NUMBER_OF_DAYS') {
return `${translate('baseline.number_days')} (${translateWithParameters(
'duration.days',
@@ -238,15 +238,26 @@ export default class App extends React.PureComponent<Props, State> {
currentSetting={currentSetting}
currentSettingValue={currentSettingValue}
days={days}
- generalSetting={generalSetting}
onSelectDays={this.handleSelectDays}
onSelectSetting={this.handleSelectSetting}
onSubmit={this.handleSubmit}
saving={saving}
selected={selected}
/>
-
- <BranchList branchLikes={this.props.branchLikes} component={this.props.component} />
+ {generalSetting && (
+ <BranchList
+ branchLikes={this.props.branchLikes}
+ component={this.props.component}
+ inheritedSetting={
+ currentSetting
+ ? {
+ type: currentSetting,
+ value: currentSettingValue
+ }
+ : generalSetting
+ }
+ />
+ )}
</div>
)}
</div>
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 c3803cacf14..4791f62ba6e 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
@@ -36,7 +36,7 @@ interface Props {
analysis: string;
branch: string;
component: string;
- onSelectAnalysis: (analysis: string) => void;
+ onSelectAnalysis: (analysis: ParsedAnalysis) => void;
}
interface State {
@@ -216,7 +216,7 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State
})}
data-date={parseDate(analysis.date).valueOf()}
key={analysis.key}
- onClick={() => this.props.onSelectAnalysis(analysis.key)}>
+ onClick={() => this.props.onSelectAnalysis(analysis)}>
<div className="branch-analysis-time spacer-right">
<TimeFormatter date={parseDate(analysis.date)} long={false}>
{formattedTime => (
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 095595ed2ff..b8dd08c6b66 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
@@ -21,8 +21,10 @@ import * as React from 'react';
import { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
import Modal from 'sonar-ui-common/components/controls/Modal';
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 BaselineSettingAnalysis from './BaselineSettingAnalysis';
import BaselineSettingDays from './BaselineSettingDays';
@@ -32,14 +34,12 @@ import BranchAnalysisList from './BranchAnalysisList';
interface Props {
branch: T.BranchWithNewCodePeriod;
component: string;
- onClose: (
- branch?: string,
- newSetting?: { type: T.NewCodePeriodSettingType; value: string | null }
- ) => void;
+ onClose: (branch?: string, newSetting?: T.NewCodePeriod) => void;
}
interface State {
analysis: string;
+ analysisDate?: Date;
days: string;
saving: boolean;
selected?: T.NewCodePeriodSettingType;
@@ -80,7 +80,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
case 'SPECIFIC_ANALYSIS':
return this.state.analysis;
default:
- return null;
+ return undefined;
}
}
@@ -88,7 +88,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
e.preventDefault();
const { branch, component } = this.props;
- const { selected: type } = this.state;
+ const { analysisDate, selected: type } = this.state;
const value = this.getSettingValue();
@@ -104,7 +104,11 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
this.setState({
saving: false
});
- this.props.onClose(branch.name, { type, value });
+ this.props.onClose(branch.name, {
+ type,
+ value,
+ effectiveValue: analysisDate && toNotSoISOString(analysisDate)
+ });
},
() => {
this.setState({
@@ -117,7 +121,8 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
requestClose = () => this.props.onClose();
- handleSelectAnalysis = (analysis: string) => this.setState({ analysis });
+ handleSelectAnalysis = (analysis: ParsedAnalysis) =>
+ this.setState({ analysis: analysis.key, analysisDate: analysis.date });
handleSelectDays = (days: string) => this.setState({ days });
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 691247fac7d..234cac81b58 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
@@ -25,12 +25,14 @@ import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { listBranchesNewCodePeriod, resetNewCodePeriod } from '../../../api/newCodePeriod';
import BranchIcon from '../../../components/icons-components/BranchIcon';
+import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import { isLongLivingBranch, isMainBranch, sortBranchesAsTree } from '../../../helpers/branches';
import BranchBaselineSettingModal from './BranchBaselineSettingModal';
interface Props {
branchLikes: T.BranchLike[];
component: T.Component;
+ inheritedSetting: T.NewCodePeriod;
}
interface State {
@@ -78,10 +80,10 @@ export default class BranchList extends React.PureComponent<Props, State> {
if (!newCodePeriod) {
return b;
}
- const { type = 'PREVIOUS_VERSION', value = null } = newCodePeriod;
+ const { type = 'PREVIOUS_VERSION', value, effectiveValue } = newCodePeriod;
return {
...b,
- newCodePeriod: { type, value }
+ newCodePeriod: { type, value, effectiveValue }
};
});
@@ -93,10 +95,7 @@ export default class BranchList extends React.PureComponent<Props, State> {
);
}
- updateBranchNewCodePeriod = (
- branch: string,
- newSetting: { type: T.NewCodePeriodSettingType; value: string | null } | undefined
- ) => {
+ updateBranchNewCodePeriod = (branch: string, newSetting: T.NewCodePeriod | undefined) => {
const { branches } = this.state;
const updated = branches.find(b => b.name === branch);
@@ -110,10 +109,7 @@ export default class BranchList extends React.PureComponent<Props, State> {
this.setState({ editedBranch: branch });
};
- closeEditModal = (
- branch?: string,
- newSetting?: { type: T.NewCodePeriodSettingType; value: string | null }
- ) => {
+ closeEditModal = (branch?: string, newSetting?: T.NewCodePeriod) => {
if (branch) {
this.setState({
branches: this.updateBranchNewCodePeriod(branch, newSetting),
@@ -133,13 +129,19 @@ export default class BranchList extends React.PureComponent<Props, State> {
});
}
- renderNewCodePeriodSetting(newCodePeriod: {
- type: T.NewCodePeriodSettingType;
- value: string | null;
- }) {
+ renderNewCodePeriodSetting(newCodePeriod: T.NewCodePeriod) {
switch (newCodePeriod.type) {
case 'SPECIFIC_ANALYSIS':
- return `${translate('baseline.specific_analysis')}: ${newCodePeriod.value}`;
+ return (
+ <>
+ {`${translate('baseline.specific_analysis')}: `}
+ {newCodePeriod.effectiveValue ? (
+ <DateTimeFormatter date={newCodePeriod.effectiveValue} />
+ ) : (
+ '?'
+ )}
+ </>
+ );
case 'NUMBER_OF_DAYS':
return `${translate('baseline.number_days')}: ${newCodePeriod.value}`;
case 'PREVIOUS_VERSION':
@@ -166,6 +168,7 @@ 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>
@@ -182,13 +185,16 @@ export default class BranchList extends React.PureComponent<Props, State> {
<div className="badge spacer-left">{translate('branches.main_branch')}</div>
)}
</td>
- <td className="huge-spacer-right nowrap">
- {branch.newCodePeriod ? (
- this.renderNewCodePeriodSetting(branch.newCodePeriod)
- ) : (
+ <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)}
+ </td>
<td className="text-right">
<ActionsDropdown>
<ActionsDropdownItem onClick={() => this.openEditModal(branch)}>
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 1a7a9759bcd..a29dfb03eba 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
@@ -29,7 +29,6 @@ export interface ProjectBaselineSelectorProps {
currentSetting?: T.NewCodePeriodSettingType;
currentSettingValue?: string | number;
days: string;
- generalSetting?: { type: T.NewCodePeriodSettingType; value?: string };
onSelectDays: (value: string) => void;
onSelectSetting: (value: T.NewCodePeriodSettingType) => void;
onSubmit: (e: React.SyntheticEvent<HTMLFormElement>) => void;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
index 449e2932e2f..02cedc53b09 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
@@ -68,7 +68,7 @@ export class CategoriesList extends React.PureComponent<Props> {
key,
name: getCategoryName(key)
}))
- .concat(FIXED_CATEGORIES);
+ .concat(!this.props.component ? FIXED_CATEGORIES : []);
const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase());
return (
<ul className="side-tabs-menu">
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx
index 9209014393f..3a2d3a956da 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx
@@ -97,7 +97,7 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> {
/>
</div>
<div className="side-tabs-main">
- {selectedCategory === 'new_code_period' ? (
+ {!this.props.component && selectedCategory === 'new_code_period' ? (
<NewCodePeriod />
) : (
<CategoryDefinitionsList
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 28fd0b461b2..82f282f8701 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
@@ -90,7 +90,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> {
const { days, selected } = this.state;
const type = selected;
- const value = type === 'NUMBER_OF_DAYS' ? days : null;
+ const value = type === 'NUMBER_OF_DAYS' ? days : undefined;
if (type) {
this.setState({ saving: true, success: false });
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx
index ac8d7740c8b..fd720c42546 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx
@@ -108,7 +108,7 @@ it('should submit correctly', async () => {
wrapper.find('form').simulate('submit', { preventDefault });
expect(preventDefault).toBeCalledTimes(1);
- expect(setNewCodePeriod).toBeCalledWith({ type: 'PREVIOUS_VERSION', value: null });
+ expect(setNewCodePeriod).toBeCalledWith({ type: 'PREVIOUS_VERSION', value: undefined });
await waitAndUpdate(wrapper);
expect(wrapper.state('currentSetting')).toEqual(wrapper.state('selected'));
});