aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorguillaume-peoch-sonarsource <guillaume.peoch@sonarsource.com>2023-10-11 11:04:43 +0200
committersonartech <sonartech@sonarsource.com>2023-10-18 20:03:05 +0000
commitc2232d5ecdc3fa0f67f916e82734b7bd2061772b (patch)
treedae1e39f7f1c95674c48e21e6e28d4a7daa0e3ed
parent15f3164036ac36318cc68fd40eab0a4633ea3675 (diff)
downloadsonarqube-c2232d5ecdc3fa0f67f916e82734b7bd2061772b.tar.gz
sonarqube-c2232d5ecdc3fa0f67f916e82734b7bd2061772b.zip
SONAR-20708 Update New Code period page to support bulk import
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/GitHub-it.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/components/AlmRepoItem.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx45
-rw-r--r--server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx17
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties5
13 files changed, 98 insertions, 32 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
index a6cb0f013fd..ead4d439536 100644
--- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
@@ -57,6 +57,7 @@ interface State {
gitlabSettings: AlmSettingsInstance[];
loading: boolean;
creatingAlmDefinition?: AlmKeys;
+ nbrOfProjects?: number;
}
const PROJECT_MODE_FOR_ALM_KEY = {
@@ -137,10 +138,12 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
this.setState({ creatingAlmDefinition: alm });
};
- handleProjectSetupDone = (createProject: CreateProjectApiCallback) => {
+ handleProjectSetupDone = (createProject: CreateProjectApiCallback, nbrOfProjects?: number) => {
const { location, router } = this.props;
this.createProjectFnRef = createProject;
+ this.setState({ nbrOfProjects });
+
location.query.setncd = 'true';
router.push(location);
};
@@ -273,7 +276,7 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
render() {
const { location, router } = this.props;
- const { creatingAlmDefinition } = this.state;
+ const { creatingAlmDefinition, nbrOfProjects } = this.state;
const mode: CreateProjectModes | undefined = location.query?.mode;
const isProjectSetupDone = location.query?.setncd === 'true';
const gridLayoutStyle = mode ? 'sw-col-start-2 sw-col-span-10' : 'sw-col-span-12';
@@ -297,6 +300,7 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
<NewCodeDefinitionSelection
router={router}
createProjectFnRef={this.createProjectFnRef}
+ numberOfProjects={nbrOfProjects}
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx
index 70ca875fe66..67e742ceae2 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx
@@ -37,7 +37,7 @@ import GitHubProjectCreateRenderer from './GitHubProjectCreateRenderer';
interface Props {
canAdmin: boolean;
loadingBindings: boolean;
- onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
+ onProjectSetupDone: (createProject: CreateProjectApiCallback, nbrOfProjects: number) => void;
almInstances: AlmSettingsInstance[];
location: Location;
router: Router;
@@ -259,16 +259,17 @@ export default class GitHubProjectCreate extends React.Component<Props, State> {
}
};
- handleImportRepository = (repoKey: string) => {
+ handleImportRepository = (repoKeys: string[]) => {
const { selectedOrganization, selectedAlmInstance } = this.state;
- if (selectedAlmInstance && selectedOrganization && repoKey !== '') {
+ if (selectedAlmInstance && selectedOrganization && repoKeys.length > 0) {
this.props.onProjectSetupDone(
setupGithubProjectCreation({
almSetting: selectedAlmInstance.key,
organization: selectedOrganization.key,
- repositoryKey: repoKey,
+ repositoryKey: repoKeys.join(','), // TBD
}),
+ repoKeys.length,
);
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
index 35847eb760e..df96843b995 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
@@ -52,7 +52,7 @@ export interface GitHubProjectCreateRendererProps {
loadingBindings: boolean;
loadingOrganizations: boolean;
loadingRepositories: boolean;
- onImportRepository: (key: string) => void;
+ onImportRepository: (key: string[]) => void;
onLoadMore: () => void;
onSearch: (q: string) => void;
onSelectOrganization: (key: string) => void;
@@ -183,7 +183,7 @@ export default function GitHubProjectCreateRenderer(props: GitHubProjectCreateRe
}
const handleImport = () => {
- props.onImportRepository(Array.from(selected).toString()); // TBD
+ props.onImportRepository(Array.from(selected));
};
const handleCheckAll = () => {
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx
index b5b52e25470..a3f008a94dd 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx
@@ -116,13 +116,13 @@ it('should show import project feature when PAT is already set', async () => {
await user.click(importButton);
expect(
- screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }),
+ screen.getByRole('heading', { name: 'onboarding.create_x_project.new_code_definition.title1' }),
).toBeInTheDocument();
await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' }));
await user.click(
screen.getByRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_project',
+ name: 'onboarding.create_project.new_code_definition.create_x_projects1',
}),
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx
index 85b5ad18a8f..51a28d6a081 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx
@@ -136,13 +136,13 @@ it('should show import project feature when PAT is already set', async () => {
await user.click(importButton);
expect(
- screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }),
+ screen.getByRole('heading', { name: 'onboarding.create_x_project.new_code_definition.title1' }),
).toBeInTheDocument();
await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' }));
await user.click(
screen.getByRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_project',
+ name: 'onboarding.create_project.new_code_definition.create_x_projects1',
}),
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx
index d7644408fa7..31cc747648e 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx
@@ -152,13 +152,13 @@ it('should show import project feature when PAT is already set', async () => {
await user.click(setupButton);
expect(
- screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }),
+ screen.getByRole('heading', { name: 'onboarding.create_x_project.new_code_definition.title1' }),
).toBeInTheDocument();
await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' }));
await user.click(
screen.getByRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_project',
+ name: 'onboarding.create_project.new_code_definition.create_x_projects1',
}),
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHub-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHub-it.tsx
index 4311a26b054..c80ae871168 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHub-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitHub-it.tsx
@@ -52,9 +52,18 @@ const ui = {
project3Checkbox: byRole('listitem', { name: 'Github repo 3' }).byRole('checkbox'),
checkAll: byRole('checkbox', { name: 'onboarding.create_project.select_all_repositories' }),
importButton: byRole('button', { name: 'onboarding.create_project.import' }),
- newCodeTitle: byRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }),
+ newCodeTitle: byRole('heading', {
+ name: 'onboarding.create_x_project.new_code_definition.title1',
+ }),
+ newCodeMultipleProjectTitle: byRole('heading', {
+ name: 'onboarding.create_x_project.new_code_definition.title2',
+ }),
+ changePeriodLaterInfo: byText('onboarding.create_projects.new_code_definition.change_info'),
createProjectButton: byRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_project',
+ name: 'onboarding.create_project.new_code_definition.create_x_projects1',
+ }),
+ createProjectsButton: byRole('button', {
+ name: 'onboarding.create_project.new_code_definition.create_x_projects2',
}),
globalSettingRadio: byRole('radio', { name: 'new_code_definition.global_setting' }),
};
@@ -202,9 +211,12 @@ it('should import several projects', async () => {
expect(ui.importButton.get()).toBeInTheDocument();
await user.click(ui.importButton.get());
- expect(await ui.newCodeTitle.find()).toBeInTheDocument();
+ expect(await ui.newCodeMultipleProjectTitle.find()).toBeInTheDocument();
+ expect(ui.changePeriodLaterInfo.get()).toBeInTheDocument();
+ expect(ui.createProjectsButton.get()).toBeDisabled();
- // TBD
+ await user.click(ui.globalSettingRadio.get());
+ expect(ui.createProjectsButton.get()).toBeEnabled();
});
it('should show search filter when the authentication is successful', async () => {
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx
index 442abc088b8..3516ac4ef33 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx
@@ -123,13 +123,13 @@ it('should show import project feature when PAT is already set', async () => {
await user.click(importButton);
expect(
- screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }),
+ screen.getByRole('heading', { name: 'onboarding.create_x_project.new_code_definition.title1' }),
).toBeInTheDocument();
await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' }));
await user.click(
screen.getByRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_project',
+ name: 'onboarding.create_project.new_code_definition.create_x_projects1',
}),
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx
index 874dc3cc1a1..e54eb0096a7 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx
@@ -53,10 +53,10 @@ const ui = {
name: /onboarding.create_project.display_name/,
}),
projectNextButton: byRole('button', { name: 'next' }),
- newCodeDefinitionHeader: byText('onboarding.create_project.new_code_definition.title'),
+ newCodeDefinitionHeader: byText('onboarding.create_x_project.new_code_definition.title1'),
inheritGlobalNcdRadio: byRole('radio', { name: 'new_code_definition.global_setting' }),
projectCreateButton: byRole('button', {
- name: 'onboarding.create_project.new_code_definition.create_project',
+ name: 'onboarding.create_project.new_code_definition.create_x_projects1',
}),
overrideNcdRadio: byRole('radio', { name: 'new_code_definition.specific_setting' }),
ncdOptionPreviousVersionRadio: byRole('radio', {
diff --git a/server/sonar-web/src/main/js/apps/create/project/components/AlmRepoItem.tsx b/server/sonar-web/src/main/js/apps/create/project/components/AlmRepoItem.tsx
index 4ae059d21c1..f604a644227 100644
--- a/server/sonar-web/src/main/js/apps/create/project/components/AlmRepoItem.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/components/AlmRepoItem.tsx
@@ -70,7 +70,7 @@ export default function AlmRepoItem({
onCheck,
onImport,
}: AlmRepoItemProps) {
- const labelId = `${almKey.replace(/\s/g, '_')}-label`;
+ const labelId = `${almKey.toString().replace(/\s/g, '_')}-label`;
return (
<RepositoryItem
selected={selected}
diff --git a/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx b/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
index b63dd993932..c9c56f65fa3 100644
--- a/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
@@ -17,9 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ButtonPrimary, Link, Spinner, Title } from 'design-system';
+import { ButtonPrimary, ButtonSecondary, FlagMessage, Link, Spinner, Title } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
+import { useNavigate } from 'react-router-dom';
import { Router } from '../../../../components/hoc/withRouter';
import NewCodeDefinitionSelector from '../../../../components/new-code-definition/NewCodeDefinitionSelector';
import { useDocUrl } from '../../../../helpers/docs';
@@ -32,16 +33,22 @@ import { CreateProjectApiCallback } from '../types';
interface Props {
createProjectFnRef: CreateProjectApiCallback | null;
router: Router;
+ numberOfProjects?: number;
}
export default function NewCodeDefinitionSelection(props: Props) {
- const { createProjectFnRef, router } = props;
+ const { createProjectFnRef, router, numberOfProjects } = props;
const [submitting, setSubmitting] = React.useState(false);
const [selectedDefinition, selectDefinition] = React.useState<NewCodeDefinitiondWithCompliance>();
+ const navigate = useNavigate();
+
const getDocUrl = useDocUrl();
+ const isMultipleProjects = numberOfProjects !== undefined && numberOfProjects !== 1;
+ const projectCount = isMultipleProjects ? numberOfProjects : 1;
+
const handleProjectCreation = React.useCallback(async () => {
if (createProjectFnRef && selectedDefinition) {
setSubmitting(true);
@@ -58,7 +65,15 @@ export default function NewCodeDefinitionSelection(props: Props) {
return (
<div id="project-ncd-selection" className="sw-body-sm">
- <Title>{translate('onboarding.create_project.new_code_definition.title')}</Title>
+ <Title>
+ <FormattedMessage
+ defaultMessage={translate('onboarding.create_x_project.new_code_definition.title')}
+ id="onboarding.create_x_project.new_code_definition.title"
+ values={{
+ count: projectCount,
+ }}
+ />
+ </Title>
<p className="sw-mb-2">
<FormattedMessage
@@ -74,15 +89,35 @@ export default function NewCodeDefinitionSelection(props: Props) {
/>
</p>
- <NewCodeDefinitionSelector onNcdChanged={selectDefinition} />
+ <NewCodeDefinitionSelector
+ onNcdChanged={selectDefinition}
+ isMultipleProjects={isMultipleProjects}
+ />
+
+ {isMultipleProjects && (
+ <FlagMessage variant="info">
+ {translate('onboarding.create_projects.new_code_definition.change_info')}
+ </FlagMessage>
+ )}
<div className="sw-mt-10 sw-mb-8">
+ <ButtonSecondary className="sw-mr-2" onClick={() => navigate(-1)}>
+ {translate('back')}
+ </ButtonSecondary>
<ButtonPrimary
onClick={handleProjectCreation}
disabled={!selectedDefinition?.isCompliant || submitting}
type="submit"
>
- {translate('onboarding.create_project.new_code_definition.create_project')}
+ <FormattedMessage
+ defaultMessage={translate(
+ 'onboarding.create_project.new_code_definition.create_x_projects',
+ )}
+ id="onboarding.create_project.new_code_definition.create_x_projects"
+ values={{
+ count: projectCount,
+ }}
+ />
<Spinner className="sw-ml-2" loading={submitting} />
</ButtonPrimary>
</div>
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 78f310ae0f2..2a2f2a810ea 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
@@ -45,10 +45,11 @@ import { NewCodeDefinitionLevels } from './utils';
interface Props {
onNcdChanged: (ncd: NewCodeDefinitiondWithCompliance) => void;
+ isMultipleProjects?: boolean;
}
export default function NewCodeDefinitionSelector(props: Props) {
- const { onNcdChanged } = props;
+ const { onNcdChanged, isMultipleProjects } = props;
const [globalNcd, setGlobalNcd] = React.useState<NewCodeDefinition | null>(null);
const [selectedNcdType, setSelectedNcdType] = React.useState<NewCodeDefinitionType | null>(null);
@@ -101,7 +102,9 @@ export default function NewCodeDefinitionSelector(props: Props) {
<PageContentFontWrapper>
<p className="sw-mt-10">
<strong className="sw-body-md-highlight">
- {translate('new_code_definition.question')}
+ {isMultipleProjects
+ ? translate('new_code_definition.question.multiple_projects')
+ : translate('new_code_definition.question')}
</strong>
</p>
<div className="sw-mt-7 sw-ml-1" role="radiogroup">
@@ -124,13 +127,19 @@ export default function NewCodeDefinitionSelector(props: Props) {
</StyledGlobalSettingWrapper>
<RadioButton
- aria-label={translate('new_code_definition.specific_setting')}
+ aria-label={
+ isMultipleProjects
+ ? translate('new_code_definition.specific_setting.multiple_projects')
+ : translate('new_code_definition.specific_setting')
+ }
checked={Boolean(selectedNcdType && selectedNcdType !== NewCodeDefinitionType.Inherited)}
className="sw-mt-12 sw-font-semibold"
onCheck={() => handleNcdChanged(NewCodeDefinitionType.PreviousVersion)}
value="specific"
>
- {translate('new_code_definition.specific_setting')}
+ {isMultipleProjects
+ ? translate('new_code_definition.specific_setting.multiple_projects')
+ : translate('new_code_definition.specific_setting')}
</RadioButton>
</div>
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index bad1ff52ecb..fb19ad89c0f 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -4031,8 +4031,10 @@ footer.web_api=Web API
#
#------------------------------------------------------------------------------
new_code_definition.question=Choose the baseline for new code for this project
+new_code_definition.question.multiple_projects=Choose the baseline for new code for those projects
new_code_definition.global_setting=Use the global setting
new_code_definition.specific_setting=Define a specific setting for this project
+new_code_definition.specific_setting.multiple_projects=Define a specific setting for your projects
new_code_definition.previous_version=Previous version
new_code_definition.previous_version.usecase=Recommended for projects following regular versions or releases.
@@ -4192,9 +4194,12 @@ onboarding.create_project.x_repositories_selected={count} {count, plural, one {r
onboarding.create_project.x_repository_created={count} {count, plural, one {repository} other {repositories}} will be created as a project on SonarQube
onboarding.create_project.new_code_definition.title=Set up project for Clean as You Code
+onboarding.create_x_project.new_code_definition.title=Set up {count, plural, one {project} other {# projects}} for Clean as You Code
onboarding.create_project.new_code_definition.description=The new code definition sets which part of your code will be considered new code. This helps you focus attention on the most recent changes to your project, enabling you to follow the Clean as You Code methodology. Learn more: {link}
onboarding.create_project.new_code_definition.description.link=Defining New Code
onboarding.create_project.new_code_definition.create_project=Create project
+onboarding.create_project.new_code_definition.create_x_projects=Create {count, plural, one {project} other {# projects}}
+onboarding.create_projects.new_code_definition.change_info=You can change this for each project individually at any time in the project administration.
onboarding.create_project.success=Congratulations! Your project has been created.