aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js
diff options
context:
space:
mode:
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>2022-05-10 12:02:41 +0200
committersonartech <sonartech@sonarsource.com>2022-05-25 20:03:16 +0000
commitc1a02f45496281a6a6ad7ab937531cc6004d9ca5 (patch)
treecf54539831791f196ff41587595b81cbae5f7123 /server/sonar-web/src/main/js
parent91726b77a19abb47414eea0969d3beeaf97fbd3d (diff)
downloadsonarqube-c1a02f45496281a6a6ad7ab937531cc6004d9ca5.tar.gz
sonarqube-c1a02f45496281a6a6ad7ab937531cc6004d9ca5.zip
SONAR-16316 New place for regulatory report
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r--server/sonar-web/src/main/js/api/mocks/BranchesServiceMock.ts45
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx16
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx36
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap221
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx153
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReportModal.tsx45
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/__tests__/RegulatoryReport-it.tsx (renamed from server/sonar-web/src/main/js/apps/projectRegulatoryReport/__tests__/RegulatoryReport-it.tsx)21
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projectRegulatoryReport/RegulatoryReport.tsx66
12 files changed, 537 insertions, 95 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/BranchesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/BranchesServiceMock.ts
new file mode 100644
index 00000000000..d833cb22e70
--- /dev/null
+++ b/server/sonar-web/src/main/js/api/mocks/BranchesServiceMock.ts
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { cloneDeep } from 'lodash';
+import { mockBranch } from '../../helpers/mocks/branch-like';
+import { BranchLike } from '../../types/branch-like';
+import { getBranches } from '../branches';
+
+export default class BranchesServiceMock {
+ branchLikes: BranchLike[];
+ defaultBranchLikes: BranchLike[] = [
+ mockBranch({ isMain: true, name: 'master' }),
+ mockBranch({ excludedFromPurge: false, name: 'delete-branch' }),
+ mockBranch({ name: 'normal-branch' })
+ ];
+
+ constructor() {
+ this.branchLikes = cloneDeep(this.defaultBranchLikes);
+ (getBranches as jest.Mock).mockImplementation(this.getBranchesHandler);
+ }
+
+ getBranchesHandler = () => {
+ return Promise.resolve(this.branchLikes);
+ };
+
+ resetBranches = () => {
+ this.branchLikes = cloneDeep(this.defaultBranchLikes);
+ };
+}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
index a3e13786bfd..7b33626b62a 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
@@ -329,8 +329,7 @@ export class Menu extends React.PureComponent<Props> {
this.renderBackgroundTasksLink(query),
this.renderUpdateKeyLink(query),
this.renderWebhooksLink(query, isProject),
- this.renderDeletionLink(query),
- this.renderRegulatoryReport(query)
+ this.renderDeletionLink(query)
];
};
@@ -542,19 +541,6 @@ export class Menu extends React.PureComponent<Props> {
);
};
- renderRegulatoryReport = (query: Query) => {
- if (!this.props.appState.regulatoryReportFeatureEnabled) {
- return null;
- }
- return (
- <li key="project_regulatory_report">
- <Link activeClassName="active" to={{ pathname: '/project/regulatory-report', query }}>
- {translate('regulatory_report.page')}
- </Link>
- </li>
- );
- };
-
renderExtension = ({ key, name }: Extension, isAdmin: boolean, baseQuery: Query) => {
const pathname = isAdmin ? `/project/admin/extension/${key}` : `/project/extension/${key}`;
const query = { ...baseQuery, qualifier: this.props.component.qualifier };
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx
index 952b65e7d1e..3010c8f5564 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx
@@ -97,6 +97,7 @@ export class ProjectInformation extends React.PureComponent<Props, State> {
canConfigureNotifications={canConfigureNotifications}
canUseBadges={canUseBadges}
component={component}
+ branchLike={branchLike}
measures={measures}
onComponentChange={this.props.onComponentChange}
onPageChange={this.setPage}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx
index 547e84bf445..8c50d12e51c 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx
@@ -18,6 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { ButtonLink } from '../../../../../components/controls/buttons';
+import ModalButton from '../../../../../components/controls/ModalButton';
import PrivacyBadgeContainer from '../../../../../components/common/PrivacyBadgeContainer';
import { translate } from '../../../../../helpers/l10n';
import { ComponentQualifier } from '../../../../../types/component';
@@ -30,18 +32,31 @@ import MetaQualityProfiles from './meta/MetaQualityProfiles';
import MetaSize from './meta/MetaSize';
import MetaTags from './meta/MetaTags';
import { ProjectInformationPages } from './ProjectInformationPages';
+import RegulatoryReportModal from './projectRegulatoryReport/RegulatoryReportModal';
+import withAppStateContext from '../../../app-state/withAppStateContext';
+import { AppState } from '../../../../../types/appstate';
+import { BranchLike } from '../../../../../types/branch-like';
export interface ProjectInformationRendererProps {
+ appState: AppState;
canConfigureNotifications: boolean;
canUseBadges: boolean;
component: Component;
+ branchLike?: BranchLike;
measures?: Measure[];
onComponentChange: (changes: {}) => void;
onPageChange: (page: ProjectInformationPages) => void;
}
export function ProjectInformationRenderer(props: ProjectInformationRendererProps) {
- const { canConfigureNotifications, canUseBadges, component, measures = [] } = props;
+ const {
+ canConfigureNotifications,
+ canUseBadges,
+ component,
+ measures = [],
+ appState,
+ branchLike
+ } = props;
const isApp = component.qualifier === ComponentQualifier.Application;
@@ -113,9 +128,26 @@ export function ProjectInformationRenderer(props: ProjectInformationRendererProp
to={ProjectInformationPages.notifications}
/>
)}
+ {component.qualifier === ComponentQualifier.Project &&
+ appState.regulatoryReportFeatureEnabled && (
+ <div className="big-padded bordered-bottom">
+ <ModalButton
+ modal={({ onClose }) => (
+ <RegulatoryReportModal
+ component={component}
+ branchLike={branchLike}
+ onClose={onClose}
+ />
+ )}>
+ {({ onClick }) => (
+ <ButtonLink onClick={onClick}>{translate('regulatory_report.page')}</ButtonLink>
+ )}
+ </ModalButton>
+ </div>
+ )}
</div>
</>
);
}
-export default React.memo(ProjectInformationRenderer);
+export default withAppStateContext(React.memo(ProjectInformationRenderer));
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx
index 42ca7b25e95..33a4b340615 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx
@@ -19,6 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../../../../helpers/testMocks';
import { mockComponent } from '../../../../../../helpers/mocks/component';
import {
ProjectInformationRenderer,
@@ -56,9 +57,22 @@ it('should handle missing quality profiles and quality gates', () => {
).toMatchSnapshot();
});
+it('should render app correctly when regulatoryReportFeatureEnabled is false', () => {
+ expect(
+ shallowRender({
+ appState: mockAppState({
+ regulatoryReportFeatureEnabled: false
+ })
+ })
+ ).toMatchSnapshot();
+});
+
function shallowRender(props: Partial<ProjectInformationRendererProps> = {}) {
return shallow(
<ProjectInformationRenderer
+ appState={mockAppState({
+ regulatoryReportFeatureEnabled: true
+ })}
canConfigureNotifications={true}
canUseBadges={true}
component={mockComponent({ qualifier: 'TRK', visibility: 'public' })}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap
index 879dcaea393..a010a9957f3 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap
@@ -2,7 +2,7 @@
exports[`should render correctly: default 1`] = `
<Fragment>
- <Memo(ProjectInformationRenderer)
+ <withAppStateContext(Component)
canConfigureNotifications={false}
canUseBadges={true}
component={
@@ -64,7 +64,7 @@ exports[`should render correctly: default 1`] = `
exports[`should render correctly: logged in user 1`] = `
<Fragment>
- <Memo(ProjectInformationRenderer)
+ <withAppStateContext(Component)
canConfigureNotifications={true}
canUseBadges={true}
component={
@@ -155,7 +155,7 @@ exports[`should render correctly: logged in user 1`] = `
exports[`should render correctly: measures loaded 1`] = `
<Fragment>
- <Memo(ProjectInformationRenderer)
+ <withAppStateContext(Component)
canConfigureNotifications={false}
canUseBadges={true}
component={
@@ -231,7 +231,7 @@ exports[`should render correctly: measures loaded 1`] = `
exports[`should render correctly: private 1`] = `
<Fragment>
- <Memo(ProjectInformationRenderer)
+ <withAppStateContext(Component)
canConfigureNotifications={false}
canUseBadges={true}
component={
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap
index b13ddca3f78..3ea8de111d9 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap
@@ -88,6 +88,15 @@ exports[`should handle missing quality profiles and quality gates 1`] = `
onPageChange={[MockFunction]}
to={2}
/>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <ModalButton
+ modal={[Function]}
+ >
+ <Component />
+ </ModalButton>
+ </div>
</div>
</Fragment>
`;
@@ -246,6 +255,15 @@ exports[`should render a private project correctly 1`] = `
onPageChange={[MockFunction]}
to={2}
/>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <ModalButton
+ modal={[Function]}
+ >
+ <Component />
+ </ModalButton>
+ </div>
</div>
</Fragment>
`;
@@ -351,6 +369,164 @@ exports[`should render an app correctly: default 1`] = `
</Fragment>
`;
+exports[`should render app correctly when regulatoryReportFeatureEnabled is false 1`] = `
+<Fragment>
+ <div>
+ <h2
+ className="big-padded bordered-bottom"
+ >
+ project.info.title
+ </h2>
+ </div>
+ <div
+ className="overflow-y-auto"
+ >
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <div
+ className="display-flex-center"
+ >
+ <h3
+ className="spacer-right"
+ >
+ project.info.description
+ </h3>
+ <PrivacyBadgeContainer
+ qualifier="TRK"
+ visibility="public"
+ />
+ </div>
+ <MetaTags
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ "visibility": "public",
+ }
+ }
+ onComponentChange={[MockFunction]}
+ />
+ </div>
+ <div
+ className="big-padded bordered-bottom it__project-loc-value"
+ >
+ <MetaSize
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ "visibility": "public",
+ }
+ }
+ measures={Array []}
+ />
+ </div>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <MetaQualityGate
+ qualityGate={
+ Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ }
+ }
+ />
+ <withLanguagesContext(MetaQualityProfiles)
+ headerClassName="big-spacer-top"
+ profiles={
+ Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ]
+ }
+ />
+ </div>
+ <MetaLinks
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ "visibility": "public",
+ }
+ }
+ />
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <MetaKey
+ componentKey="my-project"
+ qualifier="TRK"
+ />
+ </div>
+ <Memo(DrawerLink)
+ label="overview.badges.get_badge.TRK"
+ onPageChange={[MockFunction]}
+ to={1}
+ />
+ <Memo(DrawerLink)
+ label="project.info.to_notifications"
+ onPageChange={[MockFunction]}
+ to={2}
+ />
+ </div>
+</Fragment>
+`;
+
exports[`should render correctly: default 1`] = `
<Fragment>
<div>
@@ -505,6 +681,15 @@ exports[`should render correctly: default 1`] = `
onPageChange={[MockFunction]}
to={2}
/>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <ModalButton
+ modal={[Function]}
+ >
+ <Component />
+ </ModalButton>
+ </div>
</div>
</Fragment>
`;
@@ -658,6 +843,15 @@ exports[`should render correctly: no badges 1`] = `
onPageChange={[MockFunction]}
to={2}
/>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <ModalButton
+ modal={[Function]}
+ >
+ <Component />
+ </ModalButton>
+ </div>
</div>
</Fragment>
`;
@@ -806,6 +1000,15 @@ exports[`should render correctly: no badges, no notifications 1`] = `
qualifier="TRK"
/>
</div>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <ModalButton
+ modal={[Function]}
+ >
+ <Component />
+ </ModalButton>
+ </div>
</div>
</Fragment>
`;
@@ -959,6 +1162,15 @@ exports[`should render correctly: with notifications 1`] = `
onPageChange={[MockFunction]}
to={1}
/>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <ModalButton
+ modal={[Function]}
+ >
+ <Component />
+ </ModalButton>
+ </div>
</div>
</Fragment>
`;
@@ -1118,6 +1330,15 @@ exports[`should render with description 1`] = `
onPageChange={[MockFunction]}
to={2}
/>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <ModalButton
+ modal={[Function]}
+ >
+ <Component />
+ </ModalButton>
+ </div>
</div>
</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx
new file mode 100644
index 00000000000..aebf360d834
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReport.tsx
@@ -0,0 +1,153 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import classNames from 'classnames';
+import * as React from 'react';
+import { BranchLike } from '../../../../../../types/branch-like';
+import { getBranches } from '../../../../../../api/branches';
+import { getRegulatoryReportUrl } from '../../../../../../api/regulatory-report';
+import { ButtonLink } from '../../../../../../components/controls/buttons';
+import Select, { BasicSelectOption } from '../../../../../../components/controls/Select';
+import {
+ getBranchLikeDisplayName,
+ isBranch,
+ isMainBranch
+} from '../../../../../../helpers/branch-like';
+import { translate } from '../../../../../../helpers/l10n';
+import { Component } from '../../../../../../types/types';
+import { orderBy } from 'lodash';
+
+interface Props {
+ component: Pick<Component, 'key' | 'name'>;
+ branchLike?: BranchLike;
+ onClose: () => void;
+}
+
+interface State {
+ downloadStarted: boolean;
+ selectedBranch: string;
+ branchLikesOptions: BasicSelectOption[];
+}
+
+export default class RegulatoryReport extends React.PureComponent<Props, State> {
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ downloadStarted: false,
+ selectedBranch: '',
+ branchLikesOptions: []
+ };
+ }
+
+ componentDidMount() {
+ const { component, branchLike } = this.props;
+ getBranches(component.key)
+ .then(data => {
+ const mainBranch = data.find(isMainBranch);
+ const otherBranchSorted = orderBy(
+ data.filter(isBranch).filter(b => !isMainBranch(b)),
+ b => b.name
+ );
+ const sortedBranch = mainBranch ? [mainBranch, ...otherBranchSorted] : otherBranchSorted;
+ const options = sortedBranch
+ .filter(br => br.excludedFromPurge)
+ .map(br => {
+ return {
+ value: getBranchLikeDisplayName(br),
+ label: getBranchLikeDisplayName(br)
+ };
+ });
+
+ let selectedBranch = '';
+ if (branchLike && isBranch(branchLike) && branchLike.excludedFromPurge) {
+ selectedBranch = getBranchLikeDisplayName(branchLike);
+ } else if (mainBranch) {
+ selectedBranch = getBranchLikeDisplayName(mainBranch);
+ }
+ this.setState({ selectedBranch, branchLikesOptions: options });
+ })
+ .catch(() => {
+ this.setState({ branchLikesOptions: [] });
+ });
+ }
+
+ onBranchSelect = (newOption: BasicSelectOption) => {
+ this.setState({ selectedBranch: newOption.value, downloadStarted: false });
+ };
+
+ render() {
+ const { component, onClose } = this.props;
+ const { downloadStarted, selectedBranch, branchLikesOptions } = this.state;
+
+ return (
+ <>
+ <div className="modal-head">
+ <h2>{translate('regulatory_report.page')}</h2>
+ </div>
+ <div className="modal-body">
+ <p>{translate('regulatory_report.description1')}</p>
+ <div className="markdown">
+ <ul>
+ <li>{translate('regulatory_report.bullet_point1')}</li>
+ <li>{translate('regulatory_report.bullet_point2')}</li>
+ <li>{translate('regulatory_report.bullet_point3')}</li>
+ </ul>
+ </div>
+ <p>{translate('regulatory_report.description2')}</p>
+ <div className="modal-field big-spacer-top">
+ <label htmlFor="regulatory-report-branch-select">
+ {translate('regulatory_page.select_branch')}
+ </label>
+ <Select
+ className="width-100"
+ inputId="regulatory-report-branch-select"
+ id="regulatory-report-branch-select-input"
+ onChange={this.onBranchSelect}
+ options={branchLikesOptions}
+ value={branchLikesOptions.find(o => o.value === selectedBranch)}
+ />
+ </div>
+ <div className="modal-field big-spacer-top">
+ {downloadStarted && (
+ <div>
+ <p>{translate('regulatory_page.download_start.sentence')}</p>
+ </div>
+ )}
+ </div>
+ </div>
+ <div className="modal-foot">
+ <a
+ className={classNames('button button-primary big-spacer-right', {
+ disabled: downloadStarted
+ })}
+ download={[component.name, selectedBranch, 'PDF Report.zip']
+ .filter(s => !!s)
+ .join(' - ')}
+ onClick={() => this.setState({ downloadStarted: true })}
+ href={getRegulatoryReportUrl(component.key, selectedBranch)}
+ target="_blank"
+ rel="noopener noreferrer">
+ {translate('download_verb')}
+ </a>
+ <ButtonLink onClick={onClose}>{translate('cancel')}</ButtonLink>
+ </div>
+ </>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReportModal.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReportModal.tsx
new file mode 100644
index 00000000000..8906f47828c
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/RegulatoryReportModal.tsx
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from '../../../../../../helpers/l10n';
+import { Component } from '../../../../../../types/types';
+import Modal from '../../../../../../components/controls/Modal';
+import RegulatoryReport from './RegulatoryReport';
+import ClickEventBoundary from '../../../../../../components/controls/ClickEventBoundary';
+import { BranchLike } from '../../../../../../types/branch-like';
+
+interface Props {
+ component: Component;
+ branchLike?: BranchLike;
+ onClose: () => void;
+}
+
+export default function RegulatoryReportModal(props: Props) {
+ const { component, branchLike } = props;
+ return (
+ <Modal contentLabel={translate('regulatory_report.page')} onRequestClose={props.onClose}>
+ <ClickEventBoundary>
+ <form>
+ <RegulatoryReport component={component} branchLike={branchLike} onClose={props.onClose} />
+ </form>
+ </ClickEventBoundary>
+ </Modal>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projectRegulatoryReport/__tests__/RegulatoryReport-it.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/__tests__/RegulatoryReport-it.tsx
index 9b8cd7153aa..a564dcc2216 100644
--- a/server/sonar-web/src/main/js/apps/projectRegulatoryReport/__tests__/RegulatoryReport-it.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/projectRegulatoryReport/__tests__/RegulatoryReport-it.tsx
@@ -20,9 +20,20 @@
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
-import { renderComponent } from '../../../helpers/testReactTestingUtils';
+import BranchesServiceMock from '../../../../../../../api/mocks/BranchesServiceMock';
+import { renderComponent } from '../../../../../../../helpers/testReactTestingUtils';
import RegulatoryReport from '../RegulatoryReport';
+jest.mock('../../../../../../../api/branches');
+
+let handler: BranchesServiceMock;
+
+beforeAll(() => {
+ handler = new BranchesServiceMock();
+});
+
+afterEach(() => handler.resetBranches());
+
it('should open the regulatory report page', async () => {
const user = userEvent.setup();
renderRegulatoryReportApp();
@@ -30,6 +41,12 @@ it('should open the regulatory report page', async () => {
expect(screen.getByText('regulatory_report.description1')).toBeInTheDocument();
expect(screen.getByText('regulatory_report.description2')).toBeInTheDocument();
+ const branchSelect = screen.getByRole('textbox');
+ expect(branchSelect).toBeInTheDocument();
+
+ await user.click(branchSelect);
+ await user.keyboard('[ArrowDown][Enter]');
+
const downloadButton = screen.getByText('download_verb');
expect(downloadButton).toBeInTheDocument();
@@ -39,5 +56,5 @@ it('should open the regulatory report page', async () => {
});
function renderRegulatoryReportApp() {
- renderComponent(<RegulatoryReport branchLike={undefined} component={{ key: '', name: '' }} />);
+ renderComponent(<RegulatoryReport component={{ key: '', name: '' }} onClose={() => {}} />);
}
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
index 5ad4881c396..622c56db585 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
@@ -240,12 +240,6 @@ function renderComponentRoutes() {
component={lazyLoadComponent(() => import('../../apps/projectDeletion/App'))}
/>
<Route
- path="project/regulatory-report"
- component={lazyLoadComponent(() =>
- import('../../apps/projectRegulatoryReport/RegulatoryReport')
- )}
- />
- <Route
path="project/links"
component={lazyLoadComponent(() => import('../../apps/projectLinks/App'))}
/>
diff --git a/server/sonar-web/src/main/js/apps/projectRegulatoryReport/RegulatoryReport.tsx b/server/sonar-web/src/main/js/apps/projectRegulatoryReport/RegulatoryReport.tsx
deleted file mode 100644
index 55d3c489b60..00000000000
--- a/server/sonar-web/src/main/js/apps/projectRegulatoryReport/RegulatoryReport.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import classNames from 'classnames';
-import * as React from 'react';
-import { getRegulatoryReportUrl } from '../../api/regulatory-report';
-import { isBranch } from '../../helpers/branch-like';
-import { translate } from '../../helpers/l10n';
-import { BranchLike } from '../../types/branch-like';
-import { Component } from '../../types/types';
-
-interface Props {
- component: Pick<Component, 'key' | 'name'>;
- branchLike?: BranchLike;
-}
-
-function RegulatoryReport(props: Props) {
- const { component, branchLike } = props;
- const branchName = branchLike && isBranch(branchLike) ? branchLike.name : undefined;
- const [downloadStarted, setDownloadStarted] = React.useState(false);
- return (
- <div className="page page-limited">
- <header className="page-header">
- <h1 className="page-title">{translate('regulatory_report.page')}</h1>
- </header>
- <div className="page-description">
- <p>{translate('regulatory_report.description1')}</p>
- <p>{translate('regulatory_report.description2')}</p>
- <div className="big-spacer-top">
- <a
- className={classNames('button button-primary', { disabled: downloadStarted })}
- download={[component.name, branchName, 'PDF Report'].filter(s => !!s).join(' - ')}
- onClick={() => setDownloadStarted(true)}
- href={getRegulatoryReportUrl(component.key, branchName)}
- target="_blank"
- rel="noopener noreferrer">
- {translate('download_verb')}
- </a>
- {downloadStarted && (
- <div className="spacer-top">
- <p>{translate('regulatory_page.download_start.sentence')}</p>
- </div>
- )}
- </div>
- </div>
- </div>
- );
-}
-
-export default RegulatoryReport;