Переглянути джерело

SONAR-11629 Add baseline marker to activity page

tags/8.0
Jeremy Davis 4 роки тому
джерело
коміт
0cb53fe7d8
24 змінених файлів з 277 додано та 99 видалено
  1. 1
    1
      server/sonar-web/src/main/js/api/newCodePeriod.ts
  2. 9
    5
      server/sonar-web/src/main/js/app/types.d.ts
  3. 37
    22
      server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
  4. 22
    10
      server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
  5. 3
    0
      server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
  6. 6
    3
      server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.tsx
  7. 2
    0
      server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysis-test.tsx
  8. 6
    0
      server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.tsx.snap
  9. 59
    0
      server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysis-test.tsx.snap
  10. 1
    0
      server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap
  11. 31
    7
      server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css
  12. 1
    1
      server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchBaselineSettingModal-test.tsx
  13. 22
    6
      server/sonar-web/src/main/js/apps/projectBaseline/__tests__/BranchList-test.tsx
  14. 13
    3
      server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap
  15. 18
    7
      server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
  16. 2
    2
      server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx
  17. 13
    8
      server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
  18. 25
    19
      server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx
  19. 0
    1
      server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx
  20. 1
    1
      server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
  21. 1
    1
      server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx
  22. 1
    1
      server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx
  23. 1
    1
      server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx
  24. 2
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 1
- 1
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);
}

+ 9
- 5
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 =

+ 37
- 22
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>
))}

+ 22
- 10
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>
);

+ 3
- 0
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}

+ 6
- 3
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();

+ 2
- 0
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()}

+ 6
- 0
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}

+ 59
- 0
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}

+ 1
- 0
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",

+ 31
- 7
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);
}

+ 1
- 1
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();
});
});


+ 22
- 6
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}
/>
);
}

+ 13
- 3
server/sonar-web/src/main/js/apps/projectBaseline/__tests__/__snapshots__/BranchList-test.tsx.snap Переглянути файл

@@ -9,6 +9,11 @@ exports[`should render correctly 1`] = `
<tr>
<th>
branch_list.branch
</th>
<th
className="thin"
>
</th>
<th
className="thin nowrap huge-spacer-right"
@@ -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,15 +98,18 @@ exports[`should render correctly 1`] = `
/>
branch-6.7
</td>
<td
className="huge-spacer-right nowrap"
>
<td>
<span
className="badge badge-info"
>
default
</span>
</td>
<td
className="huge-spacer-right nowrap"
>
baseline.previous_version
</td>
<td
className="text-right"
>

+ 18
- 7
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>

+ 2
- 2
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 => (

+ 13
- 8
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 });


+ 25
- 19
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)}>

+ 0
- 1
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;

+ 1
- 1
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">

+ 1
- 1
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

+ 1
- 1
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 });

+ 1
- 1
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'));
});

+ 2
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties Переглянути файл

@@ -1115,6 +1115,8 @@ project_activity.delete_analysis.question=Are you sure you want to delete this a
project_activity.filter_events=Filter events
project_activity.events.tooltip.edit=Edit this event
project_activity.events.tooltip.delete=Delete this event
project_activity.new_code_period_start=New Code Period starts here
project_activity.new_code_period_start.help=The analysis before this mark is the baseline for New Code comparison

project_activity.graphs.issues=Issues
project_activity.graphs.coverage=Coverage

Завантаження…
Відмінити
Зберегти