import { translate, translateWithParameters } from '../../../helpers/l10n';
import { BranchWithNewCodePeriod } from '../../../types/branch-like';
import { NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types';
+import { isNewCodeDefinitionCompliant } from '../../../helpers/periods';
export interface BranchListRowProps {
branch: BranchWithNewCodePeriod;
);
}
+ const isCompliant = isNewCodeDefinitionCompliant(inheritedSetting);
+
return (
<tr className={settingWarning ? 'branch-setting-warning' : ''}>
<td className="nowrap">
{translate('edit')}
</ActionsDropdownItem>
{branch.newCodePeriod && (
- <ActionsDropdownItem onClick={() => props.onResetToDefault(branch.name)}>
+ <ActionsDropdownItem
+ disabled={!isCompliant}
+ onClick={() => props.onResetToDefault(branch.name)}
+ tooltipOverlay={
+ isCompliant ? null : translate('project_baseline.compliance.warning.title.project')
+ }
+ >
{translate('reset_to_default')}
</ActionsDropdownItem>
)}
branch={branchLike}
branchList={branchList}
branchesEnabled={branchSupportEnabled}
+ canAdmin={appState.canAdmin}
component={component.key}
currentSetting={currentSetting}
currentSettingValue={currentSettingValue}
import BaselineSettingPreviousVersion from './BaselineSettingPreviousVersion';
import BaselineSettingReferenceBranch from './BaselineSettingReferenceBranch';
import BranchAnalysisList from './BranchAnalysisList';
+import { isNewCodeDefinitionCompliant } from '../../../helpers/periods';
+import Tooltip from '../../../components/controls/Tooltip';
+import { FormattedMessage } from 'react-intl';
+import Link from '../../../components/common/Link';
export interface ProjectBaselineSelectorProps {
analysis?: string;
branch: Branch;
branchList: Branch[];
branchesEnabled?: boolean;
+ canAdmin: boolean | undefined;
component: string;
currentSetting?: NewCodePeriodSettingType;
currentSettingValue?: string;
branch,
branchList,
branchesEnabled,
+ canAdmin,
component,
currentSetting,
currentSettingValue,
selected,
} = props;
+ const isGeneralSettingCompliant = isNewCodeDefinitionCompliant(generalSetting);
+
const { isChanged, isValid } = validateSetting({
analysis,
currentSetting,
<Radio
checked={!overrideGeneralSetting}
className="big-spacer-bottom"
+ disabled={!isGeneralSettingCompliant}
onCheck={() => props.onToggleSpecificSetting(false)}
value="general"
>
- {translate('project_baseline.general_setting')}
+ <Tooltip
+ overlay={
+ isGeneralSettingCompliant
+ ? null
+ : translate('project_baseline.compliance.warning.title.global')
+ }
+ >
+ <span>{translate('project_baseline.global_setting')}</span>
+ </Tooltip>
</Radio>
<div className="big-spacer-left">{renderGeneralSetting(generalSetting)}</div>
+ {!isGeneralSettingCompliant && (
+ <Alert className="sw-mt-10 sw-max-w-[700px]" variant="warning">
+ <p className="sw-mb-2 sw-font-bold">
+ {translate('project_baseline.compliance.warning.title.global')}
+ </p>
+ <p className="sw-mb-2">
+ {canAdmin ? (
+ <FormattedMessage
+ id="project_baseline.compliance.warning.explanation.admin"
+ defaultMessage={translate(
+ 'project_baseline.compliance.warning.explanation.admin'
+ )}
+ values={{
+ link: (
+ <Link to="/admin/settings?category=new_code_period">
+ {translate('project_baseline.warning.explanation.action.admin.link')}
+ </Link>
+ ),
+ }}
+ />
+ ) : (
+ translate('project_baseline.compliance.warning.explanation')
+ )}
+ </p>
+ </Alert>
+ )}
<Radio
checked={overrideGeneralSetting}
} from '../../../../helpers/testReactTestingUtils';
import { Feature } from '../../../../types/features';
import routes from '../../routes';
+import { NewCodePeriodSettingType } from '../../../../types/types';
jest.mock('../../../../api/newCodePeriod');
jest.mock('../../../../api/projectActivity');
expect(ui.referenceBranchRadio.query()).not.toBeInTheDocument();
});
+it('prevents selection of global setting if it is not compliant and warns non-admin about it', async () => {
+ codePeriodsMock.setNewCodePeriod({
+ type: NewCodePeriodSettingType.NUMBER_OF_DAYS,
+ value: '99',
+ inherited: true,
+ });
+
+ const { ui } = getPageObjects();
+ renderProjectBaselineApp();
+ await ui.appIsLoaded();
+
+ expect(ui.generalSettingRadio.get()).toBeChecked();
+ expect(ui.generalSettingRadio.get()).toHaveClass('disabled');
+ expect(ui.complianceWarning.get()).toBeVisible();
+});
+
+it('prevents selection of global setting if it is not compliant and warns admin about it', async () => {
+ codePeriodsMock.setNewCodePeriod({
+ type: NewCodePeriodSettingType.NUMBER_OF_DAYS,
+ value: '99',
+ inherited: true,
+ });
+
+ const { ui } = getPageObjects();
+ renderProjectBaselineApp({ appState: mockAppState({ canAdmin: true }) });
+ await ui.appIsLoaded();
+
+ expect(ui.generalSettingRadio.get()).toBeChecked();
+ expect(ui.generalSettingRadio.get()).toHaveClass('disabled');
+ expect(ui.complianceWarningAdmin.get()).toBeVisible();
+ expect(ui.complianceWarning.query()).not.toBeInTheDocument();
+});
+
it('renders correctly with branch support feature', async () => {
const { ui } = getPageObjects();
renderProjectBaselineApp({
pageHeading: byRole('heading', { name: 'project_baseline.page' }),
branchListHeading: byRole('heading', { name: 'project_baseline.default_setting' }),
generalSettingsLink: byRole('link', { name: 'project_baseline.page.description2.link' }),
- generalSettingRadio: byRole('radio', { name: 'project_baseline.general_setting' }),
+ generalSettingRadio: byRole('radio', { name: 'project_baseline.global_setting' }),
specificSettingRadio: byRole('radio', { name: 'project_baseline.specific_setting' }),
previousVersionRadio: byRole('radio', { name: /baseline.previous_version.description/ }),
numberDaysRadio: byRole('radio', { name: /baseline.number_days.description/ }),
editButton: byRole('button', { name: 'edit' }),
resetToDefaultButton: byRole('button', { name: 'reset_to_default' }),
saved: byText('settings.state.saved'),
+ complianceWarningAdmin: byText('project_baseline.compliance.warning.explanation.admin'),
+ complianceWarning: byText('project_baseline.compliance.warning.explanation'),
};
async function appIsLoaded() {
className?: string;
children: React.ReactNode;
destructive?: boolean;
+ disabled?: boolean;
label?: string;
tooltipOverlay?: React.ReactNode;
tooltipPlacement?: Placement;
children = (
<ButtonPlain
className={className}
+ disabled={this.props.disabled}
preventDefault={true}
id={this.props.id}
onClick={this.handleClick}
border: 0;
}
+.button-plain:disabled {
+ color: var(--disableGrayText) !important;
+ cursor: not-allowed !important;
+}
+
/* #endregion */
/* #region .button-link */
project_baseline.page.description2.link=General Settings
project_baseline.page.question=What should be the baseline for new code for this project?
project_baseline.default_setting=Project setting
-project_baseline.general_setting=Use the general setting
+project_baseline.global_setting=Use the global setting
project_baseline.specific_setting=Define a specific setting for this project
project_baseline.configure_branches=Set a specific setting for a branch
+project_baseline.compliance.warning.title.project=Your project new code definition is not compliant with the Clean as You Code methodology
+project_baseline.compliance.warning.title.global=Your global new code definition is not compliant with the Clean as You Code methodology
+project_baseline.compliance.warning.explanation=Please ask an administrator to update the global new code definition before switching back to it.
+project_baseline.compliance.warning.explanation.admin=Please update the global new code definition under {link} before switching back to it.
+project_baseline.warning.explanation.action.admin.link=General Settings > New Code
+
baseline.previous_version=Previous version
baseline.previous_version.usecase=Recommended for projects following regular versions or releases.
baseline.previous_version.description=Any code that has changed since the previous version is considered new code.