From: Philippe Perrin Date: Thu, 29 Jun 2023 13:33:54 +0000 (+0200) Subject: SONAR-19583 Display a green check if number of days new code definition value is... X-Git-Tag: 10.2.0.77647~487 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0b2424d5516097f0251ac77a52d3a27776f4ee89;p=sonarqube.git SONAR-19583 Display a green check if number of days new code definition value is set to a compliant value --- 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 063a19f0639..e80de3b008c 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 @@ -17,6 +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 { noop } from 'lodash'; import * as React from 'react'; import { setNewCodePeriod } from '../../../api/newCodePeriod'; import Modal from '../../../components/controls/Modal'; @@ -30,7 +31,6 @@ import { translate, translateWithParameters } from '../../../helpers/l10n'; import { getNumberOfDaysDefaultValue } from '../../../helpers/new-code-definition'; import { Branch, BranchWithNewCodePeriod } from '../../../types/branch-like'; import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; -import { ParsedAnalysis } from '../../../types/project-activity'; import { getSettingValue, validateSetting } from '../utils'; import BaselineSettingAnalysis from './BaselineSettingAnalysis'; import BaselineSettingReferenceBranch from './BaselineSettingReferenceBranch'; @@ -49,6 +49,7 @@ interface State { analysis: string; analysisDate?: Date; days: string; + isChanged: boolean; referenceBranch: string; saving: boolean; selected?: NewCodeDefinitionType; @@ -69,6 +70,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent this.props.onClose(); - handleSelectAnalysis = (analysis: ParsedAnalysis) => - this.setState({ analysis: analysis.key, analysisDate: analysis.date }); + handleSelectDays = (days: string) => this.setState({ days, isChanged: true }); - handleSelectDays = (days: string) => this.setState({ days }); + handleSelectReferenceBranch = (referenceBranch: string) => + this.setState({ referenceBranch, isChanged: true }); - handleSelectReferenceBranch = (referenceBranch: string) => this.setState({ referenceBranch }); - - handleSelectSetting = (selected: NewCodeDefinitionType) => this.setState({ selected }); + handleSelectSetting = (selected: NewCodeDefinitionType) => { + this.setState((currentState) => ({ selected, isChanged: selected !== currentState.selected })); + }; render() { const { branch, branchList } = this.props; - const { analysis, days, referenceBranch, saving, selected } = this.state; + const { analysis, days, isChanged, referenceBranch, saving, selected } = this.state; const header = translateWithParameters('baseline.new_code_period_for_branch_x', branch.name); const currentSetting = branch.newCodePeriod?.type; const currentSettingValue = branch.newCodePeriod?.value; - const { isChanged, isValid } = validateSetting({ - analysis, - currentSetting, - currentSettingValue, + const isValid = validateSetting({ days, referenceBranch, selected, @@ -203,7 +203,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent {currentSetting === NewCodeDefinitionType.SpecificAnalysis && ( {}} + onSelect={noop} selected={selected === NewCodeDefinitionType.SpecificAnalysis} /> )} @@ -213,7 +213,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent )} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx index 5d185dd51ca..f28d2d251bd 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx @@ -40,7 +40,6 @@ import { AppState } from '../../../types/appstate'; import { Branch, BranchLike } from '../../../types/branch-like'; import { Feature } from '../../../types/features'; import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; -import { ParsedAnalysis } from '../../../types/project-activity'; import { Component } from '../../../types/types'; import '../styles.css'; import { getSettingValue } from '../utils'; @@ -62,6 +61,7 @@ interface State { currentSettingValue?: string; days: string; generalSetting?: NewCodeDefinition; + isChanged: boolean; loading: boolean; overrideGeneralSetting?: boolean; referenceBranch?: string; @@ -75,6 +75,7 @@ class ProjectBaselineApp extends React.PureComponent { state: State = { branchList: [], days: getNumberOfDaysDefaultValue(), + isChanged: false, loading: true, saving: false, }; @@ -113,6 +114,7 @@ class ProjectBaselineApp extends React.PureComponent { currentSetting, currentSettingValue, generalSetting, + isChanged: false, selected: currentSetting || generalSetting.type, overrideGeneralSetting: Boolean(currentSetting), days: @@ -175,6 +177,7 @@ class ProjectBaselineApp extends React.PureComponent { this.setState({ saving: false, currentSetting: undefined, + isChanged: false, selected: undefined, success: true, }); @@ -186,12 +189,10 @@ class ProjectBaselineApp extends React.PureComponent { ); }; - handleSelectAnalysis = (analysis: ParsedAnalysis) => this.setState({ analysis: analysis.key }); - - handleSelectDays = (days: string) => this.setState({ days }); + handleSelectDays = (days: string) => this.setState({ days, isChanged: true }); handleSelectReferenceBranch = (referenceBranch: string) => { - this.setState({ referenceBranch }); + this.setState({ referenceBranch, isChanged: true }); }; handleCancel = () => @@ -203,23 +204,31 @@ class ProjectBaselineApp extends React.PureComponent { }) => this.getUpdatedState({ generalSetting, currentSetting, currentSettingValue }) ); - handleSelectSetting = (selected?: NewCodeDefinitionType) => this.setState({ selected }); + handleSelectSetting = (selected?: NewCodeDefinitionType) => { + this.setState((currentState) => ({ + selected, + isChanged: selected !== currentState.selected, + })); + }; handleToggleSpecificSetting = (overrideGeneralSetting: boolean) => - this.setState({ overrideGeneralSetting }); + this.setState((currentState) => ({ + overrideGeneralSetting, + isChanged: currentState.overrideGeneralSetting !== overrideGeneralSetting, + })); handleSubmit = (e: React.SyntheticEvent) => { e.preventDefault(); const { component } = this.props; - const { analysis, days, selected: type, referenceBranch, overrideGeneralSetting } = this.state; + const { days, selected: type, referenceBranch, overrideGeneralSetting } = this.state; if (!overrideGeneralSetting) { this.resetSetting(); return; } - const value = getSettingValue({ type, analysis, days, referenceBranch }); + const value = getSettingValue({ type, days, referenceBranch }); if (type) { this.setState({ saving: true }); @@ -233,6 +242,7 @@ class ProjectBaselineApp extends React.PureComponent { saving: false, currentSetting: type, currentSettingValue: value || undefined, + isChanged: false, success: true, }); this.resetSuccess(); @@ -252,6 +262,7 @@ class ProjectBaselineApp extends React.PureComponent { currentSetting, days, generalSetting, + isChanged, loading, currentSettingValue, overrideGeneralSetting, @@ -286,8 +297,8 @@ class ProjectBaselineApp extends React.PureComponent { currentSettingValue={currentSettingValue} days={days} generalSetting={generalSetting} + isChanged={isChanged} onCancel={this.handleCancel} - onSelectAnalysis={this.handleSelectAnalysis} onSelectDays={this.handleSelectDays} onSelectReferenceBranch={this.handleSelectReferenceBranch} onSelectSetting={this.handleSelectSetting} 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 39f6c718421..e16920aae9c 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 @@ -19,6 +19,7 @@ */ import classNames from 'classnames'; import { RadioButton } from 'design-system'; +import { noop } from 'lodash'; import * as React from 'react'; import Tooltip from '../../../components/controls/Tooltip'; import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; @@ -32,7 +33,6 @@ import { translate } from '../../../helpers/l10n'; import { isNewCodeDefinitionCompliant } from '../../../helpers/new-code-definition'; import { Branch } from '../../../types/branch-like'; import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; -import { ParsedAnalysis } from '../../../types/project-activity'; import { validateSetting } from '../utils'; import BaselineSettingAnalysis from './BaselineSettingAnalysis'; import BaselineSettingReferenceBranch from './BaselineSettingReferenceBranch'; @@ -49,8 +49,8 @@ export interface ProjectBaselineSelectorProps { currentSettingValue?: string; days: string; generalSetting: NewCodeDefinition; + isChanged: boolean; onCancel: () => void; - onSelectAnalysis: (analysis: ParsedAnalysis) => void; onSelectDays: (value: string) => void; onSelectReferenceBranch: (value: string) => void; onSelectSetting: (value?: NewCodeDefinitionType) => void; @@ -78,6 +78,7 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr currentSettingValue, days, generalSetting, + isChanged, overrideGeneralSetting, referenceBranch, saving, @@ -86,10 +87,7 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr const isGlobalNcdCompliant = isNewCodeDefinitionCompliant(generalSetting); - const { isChanged, isValid } = validateSetting({ - analysis, - currentSetting, - currentSettingValue, + const isValid = validateSetting({ days, overrideGeneralSetting, referenceBranch, @@ -172,7 +170,7 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr )} {!branchesEnabled && currentSetting === NewCodeDefinitionType.SpecificAnalysis && ( {}} + onSelect={noop} selected={ overrideGeneralSetting && selected === NewCodeDefinitionType.SpecificAnalysis } @@ -186,7 +184,7 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr analysis={analysis || ''} branch={branch.name} component={component} - onSelectAnalysis={props.onSelectAnalysis} + onSelectAnalysis={noop} /> )} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx index edcad7d025b..d1fc4e010eb 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx @@ -276,9 +276,7 @@ it('can set a reference branch setting for branch', async () => { await ui.setBranchReferenceToBranchSetting('main', 'feature'); - expect( - within(byRole('table').get()).getByText('baseline.reference_branch: feature') - ).toBeInTheDocument(); + expect(byRole('table').byText('baseline.reference_branch: feature').get()).toBeInTheDocument(); }); function renderProjectBaselineApp(context: RenderContext = {}) { diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts index 1f50739f93c..b0bd20f5c17 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts @@ -54,93 +54,63 @@ describe('getSettingValue', () => { describe('validateSettings', () => { it('should validate at branch level', () => { - expect(validateSetting({ days: '' })).toEqual({ isChanged: false, isValid: false }); + expect(validateSetting({ days: '' })).toEqual(false); expect( validateSetting({ - currentSetting: NewCodeDefinitionType.PreviousVersion, days: '12', selected: NewCodeDefinitionType.NumberOfDays, }) - ).toEqual({ isChanged: true, isValid: true }); + ).toEqual(true); expect( validateSetting({ - currentSetting: NewCodeDefinitionType.PreviousVersion, days: 'nope', selected: NewCodeDefinitionType.NumberOfDays, }) - ).toEqual({ isChanged: true, isValid: false }); + ).toEqual(false); expect( validateSetting({ - currentSetting: NewCodeDefinitionType.NumberOfDays, - currentSettingValue: '15', - days: '15', - selected: NewCodeDefinitionType.NumberOfDays, - }) - ).toEqual({ isChanged: false, isValid: true }); - expect( - validateSetting({ - currentSetting: NewCodeDefinitionType.NumberOfDays, - currentSettingValue: '15', - days: '13', - selected: NewCodeDefinitionType.NumberOfDays, - }) - ).toEqual({ isChanged: true, isValid: true }); - expect( - validateSetting({ - analysis: 'analysis1', - currentSetting: NewCodeDefinitionType.SpecificAnalysis, - currentSettingValue: 'analysis1', days: '', selected: NewCodeDefinitionType.SpecificAnalysis, }) - ).toEqual({ isChanged: false, isValid: false }); + ).toEqual(false); expect( validateSetting({ - analysis: 'analysis2', - currentSetting: NewCodeDefinitionType.SpecificAnalysis, - currentSettingValue: 'analysis1', - days: '', - selected: NewCodeDefinitionType.SpecificAnalysis, - }) - ).toEqual({ isChanged: true, isValid: false }); - expect( - validateSetting({ - currentSetting: NewCodeDefinitionType.ReferenceBranch, - currentSettingValue: 'master', days: '', referenceBranch: 'master', selected: NewCodeDefinitionType.ReferenceBranch, }) - ).toEqual({ isChanged: false, isValid: true }); + ).toEqual(true); expect( validateSetting({ - currentSetting: NewCodeDefinitionType.ReferenceBranch, - currentSettingValue: 'master', days: '', referenceBranch: '', selected: NewCodeDefinitionType.ReferenceBranch, }) - ).toEqual({ isChanged: true, isValid: false }); + ).toEqual(false); }); 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({ days: '', overrideGeneralSetting: false })).toEqual(true); + expect( + validateSetting({ + selected: NewCodeDefinitionType.PreviousVersion, + days: '', + overrideGeneralSetting: true, + }) + ).toEqual(true); expect( validateSetting({ - currentSetting: NewCodeDefinitionType.PreviousVersion, + selected: NewCodeDefinitionType.NumberOfDays, days: '', - overrideGeneralSetting: false, + overrideGeneralSetting: true, + }) + ).toEqual(false); + expect( + validateSetting({ + selected: NewCodeDefinitionType.NumberOfDays, + days: '12', + overrideGeneralSetting: true, }) - ).toEqual({ - isChanged: true, - isValid: true, - }); + ).toEqual(true); }); }); 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 6a1076de85b..45c5c8efd94 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts +++ b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts @@ -44,46 +44,20 @@ export function getSettingValue({ } export function validateSetting(state: { - analysis?: string; - currentSetting?: NewCodeDefinitionType; - currentSettingValue?: string; days: string; overrideGeneralSetting?: boolean; referenceBranch?: string; selected?: NewCodeDefinitionType; }) { - const { - analysis = '', - currentSetting, - currentSettingValue, - days, - overrideGeneralSetting, - referenceBranch = '', - selected, - } = state; - - let isChanged; - if (!currentSetting && overrideGeneralSetting !== undefined) { - isChanged = overrideGeneralSetting; - } else { - isChanged = - overrideGeneralSetting === false || - selected !== currentSetting || - (selected === NewCodeDefinitionType.NumberOfDays && days !== currentSettingValue) || - (selected === NewCodeDefinitionType.SpecificAnalysis && analysis !== currentSettingValue) || - (selected === NewCodeDefinitionType.ReferenceBranch && - referenceBranch !== currentSettingValue); - } + const { days, overrideGeneralSetting, referenceBranch = '', selected } = state; - const isValid = + return ( overrideGeneralSetting === false || (!!selected && isNewCodeDefinitionCompliant({ type: selected, value: days, }) && - (selected !== NewCodeDefinitionType.SpecificAnalysis || analysis.length > 0) && - (selected !== NewCodeDefinitionType.ReferenceBranch || referenceBranch.length > 0)); - - return { isChanged, isValid }; + (selected !== NewCodeDefinitionType.ReferenceBranch || referenceBranch.length > 0)) + ); } 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 7dd692db76f..b6495cbc1f7 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 @@ -39,6 +39,7 @@ interface State { days: string; loading: boolean; currentSettingValue?: string; + isChanged: boolean; saving: boolean; selected?: NewCodeDefinitionType; success: boolean; @@ -49,6 +50,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { state: State = { loading: true, days: getNumberOfDaysDefaultValue(), + isChanged: false, saving: false, success: false, }; @@ -79,15 +81,20 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { } onSelectDays = (days: string) => { - this.setState({ days, success: false }); + this.setState({ days, success: false, isChanged: true }); }; onSelectSetting = (selected: NewCodeDefinitionType) => { - this.setState({ selected, success: false }); + this.setState((currentState) => ({ + selected, + success: false, + isChanged: selected !== currentState.selected, + })); }; onCancel = () => { this.setState(({ currentSetting, currentSettingValue, days }) => ({ + isChanged: false, selected: currentSetting, days: currentSetting === NewCodeDefinitionType.NumberOfDays ? String(currentSettingValue) : days, @@ -113,6 +120,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { saving: false, currentSetting: type, currentSettingValue: value || undefined, + isChanged: false, success: true, }); } @@ -128,12 +136,16 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { }; render() { - const { currentSetting, days, loading, currentSettingValue, saving, selected, success } = - this.state; - - const isChanged = - selected !== currentSetting || - (selected === NewCodeDefinitionType.NumberOfDays && String(days) !== currentSettingValue); + const { + currentSetting, + days, + loading, + isChanged, + currentSettingValue, + saving, + selected, + success, + } = this.state; const isValid = selected !== NewCodeDefinitionType.NumberOfDays || diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx index 4a12a6a042a..2f4660713f6 100644 --- a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx +++ b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx @@ -49,24 +49,18 @@ export default function NewCodeDefinitionSelector(props: Props) { const [globalNcd, setGlobalNcd] = React.useState(null); const [selectedNcdType, setSelectedNcdType] = React.useState(null); const [days, setDays] = React.useState(''); + const [isChanged, setIsChanged] = React.useState(false); const isGlobalNcdCompliant = React.useMemo( () => Boolean(globalNcd && isNewCodeDefinitionCompliant(globalNcd)), [globalNcd] ); - const initialNumberOfDays = React.useMemo(() => { + React.useEffect(() => { const numberOfDays = getNumberOfDaysDefaultValue(globalNcd); setDays(numberOfDays); - - return numberOfDays; }, [globalNcd]); - const isChanged = React.useMemo( - () => selectedNcdType === NewCodeDefinitionType.NumberOfDays && days !== initialNumberOfDays, - [selectedNcdType, days, initialNumberOfDays] - ); - const isCompliant = React.useMemo( () => !!selectedNcdType && @@ -77,6 +71,16 @@ export default function NewCodeDefinitionSelector(props: Props) { [selectedNcdType, days] ); + const handleNcdChanged = React.useCallback( + (newNcdType: NewCodeDefinitionType) => { + if (newNcdType && newNcdType !== selectedNcdType) { + setSelectedNcdType(newNcdType); + setIsChanged(true); + } + }, + [selectedNcdType] + ); + React.useEffect(() => { function fetchGlobalNcd() { getNewCodePeriod().then(setGlobalNcd, noop); @@ -105,7 +109,7 @@ export default function NewCodeDefinitionSelector(props: Props) { checked={selectedNcdType === NewCodeDefinitionType.Inherited} className="big-spacer-bottom" disabled={!isGlobalNcdCompliant} - onCheck={() => setSelectedNcdType(NewCodeDefinitionType.Inherited)} + onCheck={() => handleNcdChanged(NewCodeDefinitionType.Inherited)} value="general" > setSelectedNcdType(NewCodeDefinitionType.PreviousVersion)} + onCheck={() => handleNcdChanged(NewCodeDefinitionType.PreviousVersion)} value="specific" > {translate('new_code_definition.specific_setting')} @@ -146,7 +150,7 @@ export default function NewCodeDefinitionSelector(props: Props) { disabled={Boolean( !selectedNcdType || selectedNcdType === NewCodeDefinitionType.Inherited )} - onSelect={setSelectedNcdType} + onSelect={handleNcdChanged} selected={selectedNcdType === NewCodeDefinitionType.PreviousVersion} /> @@ -158,7 +162,7 @@ export default function NewCodeDefinitionSelector(props: Props) { isChanged={isChanged} isValid={isCompliant} onChangeDays={setDays} - onSelect={setSelectedNcdType} + onSelect={handleNcdChanged} selected={selectedNcdType === NewCodeDefinitionType.NumberOfDays} /> @@ -167,7 +171,7 @@ export default function NewCodeDefinitionSelector(props: Props) { disabled={Boolean( !selectedNcdType || selectedNcdType === NewCodeDefinitionType.Inherited )} - onClick={() => setSelectedNcdType(NewCodeDefinitionType.ReferenceBranch)} + onClick={() => handleNcdChanged(NewCodeDefinitionType.ReferenceBranch)} selected={selectedNcdType === NewCodeDefinitionType.ReferenceBranch} title={translate('new_code_definition.reference_branch')} >