]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22708 Add calculation change banner on projects and portfolios
authorIsmail Cherri <ismail.cherri@sonarsource.com>
Fri, 16 Aug 2024 15:11:00 +0000 (18:11 +0300)
committersonartech <sonartech@sonarsource.com>
Mon, 26 Aug 2024 20:03:07 +0000 (20:03 +0000)
server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
server/sonar-web/src/main/js/app/components/__tests__/CalculationChangeMessage-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 022125a7d715d3ee390ac9056fe119ae2ea4c6d7..0944705d8dee03aeb04e10f2447f61eb45469e9f 100644 (file)
@@ -28,15 +28,16 @@ import A11ySkipLinks from '~sonar-aligned/components/a11y/A11ySkipLinks';
 import SuggestionsProvider from '../../components/embed-docs-modal/SuggestionsProvider';
 import NCDAutoUpdateMessage from '../../components/new-code-definition/NCDAutoUpdateMessage';
 import Workspace from '../../components/workspace/Workspace';
+import CalculationChangeMessage from './calculation-notification/CalculationChangeMessage';
 import GlobalFooter from './GlobalFooter';
-import StartupModal from './StartupModal';
-import SystemAnnouncement from './SystemAnnouncement';
 import IndexationContextProvider from './indexation/IndexationContextProvider';
 import IndexationNotification from './indexation/IndexationNotification';
 import LanguagesContextProvider from './languages/LanguagesContextProvider';
 import MetricsContextProvider from './metrics/MetricsContextProvider';
 import GlobalNav from './nav/global/GlobalNav';
 import PromotionNotification from './promotion-notification/PromotionNotification';
+import StartupModal from './StartupModal';
+import SystemAnnouncement from './SystemAnnouncement';
 import UpdateNotification from './update-notification/UpdateNotification';
 
 /*
@@ -98,6 +99,7 @@ export default function GlobalContainer() {
                         <NCDAutoUpdateMessage />
                         <UpdateNotification dismissable />
                         <GlobalNav location={location} />
+                        <CalculationChangeMessage />
                         {/* The following is the portal anchor point for the component nav
                          * See ComponentContainer.tsx
                          */}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/CalculationChangeMessage-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/CalculationChangeMessage-test.tsx
new file mode 100644 (file)
index 0000000..db90f56
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 React from 'react';
+import { Outlet, Route } from 'react-router-dom';
+import { byRole, byText } from '~sonar-aligned/helpers/testSelector';
+import { ComponentQualifier } from '~sonar-aligned/types/component';
+import SettingsServiceMock from '../../../api/mocks/SettingsServiceMock';
+import { renderAppRoutes } from '../../../helpers/testReactTestingUtils';
+import { SettingsKey } from '../../../types/settings';
+import CalculationChangeMessage from '../calculation-notification/CalculationChangeMessage';
+
+const ui = {
+  alert: byRole('alert'),
+  learnMoreLink: byRole('link', { name: 'learn_more open_in_new_tab' }),
+
+  alertText: (qualifier: string) => byText(`notification.calculation_change.message.${qualifier}`),
+};
+
+const settingsHandler = new SettingsServiceMock();
+
+beforeEach(() => {
+  settingsHandler.reset();
+});
+
+it.each([
+  ['Project', '/projects', ComponentQualifier.Project],
+  ['Portfolios', '/portfolios', ComponentQualifier.Portfolio],
+])('should render on %s page', (_, path, qualifier) => {
+  render(path);
+  expect(ui.alert.get()).toBeInTheDocument();
+  expect(ui.alertText(qualifier).get()).toBeInTheDocument();
+  expect(ui.learnMoreLink.get()).toBeInTheDocument();
+});
+
+it.each([
+  ['Project', '/projects', ComponentQualifier.Project],
+  ['Portfolios', '/portfolios', ComponentQualifier.Portfolio],
+])('should not render on %s page if isLegacy', (_, path, qualifier) => {
+  settingsHandler.set(SettingsKey.LegacyMode, 'true');
+  render(path);
+  expect(ui.alert.get()).toBeInTheDocument();
+  expect(ui.alertText(qualifier).get()).toBeInTheDocument();
+  expect(ui.learnMoreLink.get()).toBeInTheDocument();
+});
+
+it('should not render on other page', () => {
+  render('/other');
+  expect(ui.alert.query()).not.toBeInTheDocument();
+  expect(ui.alertText(ComponentQualifier.Project).query()).not.toBeInTheDocument();
+  expect(ui.learnMoreLink.query()).not.toBeInTheDocument();
+});
+
+function render(indexPath = '/projects') {
+  renderAppRoutes(indexPath, () => (
+    <Route
+      path="/"
+      element={
+        <>
+          <CalculationChangeMessage />
+          <Outlet />
+        </>
+      }
+    >
+      <Route path="projects" element={<div>Projects</div>} />
+      <Route path="portfolios" element={<div>Portfolios</div>} />
+      <Route path="other" element={<div>Other page</div>} />
+    </Route>
+  ));
+}
diff --git a/server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx b/server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx
new file mode 100644 (file)
index 0000000..34e97a1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { useLocation } from '~sonar-aligned/components/hoc/withRouter';
+import { ComponentQualifier } from '~sonar-aligned/types/component';
+import DocumentationLink from '../../../components/common/DocumentationLink';
+import DismissableAlert from '../../../components/ui/DismissableAlert';
+import { DocLink } from '../../../helpers/doc-links';
+import { translate } from '../../../helpers/l10n';
+import { useIsLegacyCCTMode } from '../../../queries/settings';
+import { Dict } from '../../../types/types';
+
+const SHOW_MESSAGE_PATHS: Dict<ComponentQualifier> = {
+  '/projects': ComponentQualifier.Project,
+  '/portfolios': ComponentQualifier.Portfolio,
+};
+
+const ALERT_KEY = 'sonarqube.dismissed_calculation_change_alert';
+
+export default function CalculationChangeMessage() {
+  const location = useLocation();
+  const { data: isLegacy } = useIsLegacyCCTMode();
+
+  if (isLegacy || !Object.keys(SHOW_MESSAGE_PATHS).includes(location.pathname)) {
+    return null;
+  }
+
+  return (
+    <DismissableAlert variant="info" alertKey={ALERT_KEY + SHOW_MESSAGE_PATHS[location.pathname]}>
+      <FormattedMessage
+        id={`notification.calculation_change.message.${SHOW_MESSAGE_PATHS[location.pathname]}`}
+        values={{
+          link: (
+            <DocumentationLink to={DocLink.MetricDefinitions}>
+              {translate('learn_more')}
+            </DocumentationLink>
+          ),
+        }}
+      />
+    </DismissableAlert>
+  );
+}
index 60f76ff541c9b9ec413767961b2578823caa5341..f3e0125f092f7a3c9a664ec208b648145054d9d6 100644 (file)
@@ -38,6 +38,8 @@ export default function DismissableAlert(props: DismissableAlertProps) {
   React.useEffect(() => {
     if (get(DISMISSED_ALERT_STORAGE_KEY, alertKey) !== 'true') {
       setShow(true);
+    } else {
+      setShow(false);
     }
   }, [alertKey]);
 
index 3463c9e1c22aa4c450a3bc320852093a64d699dc..41ac785abeb29e4b3af2dd8c41ed715731b10c48 100644 (file)
@@ -2705,6 +2705,8 @@ notification.dispatcher.SQ-MyNewIssues=My new issues
 notification.dispatcher.CeReportTaskFailure=Background tasks in failure on my administered projects
 notification.dispatcher.CeReportTaskFailure.project=Background tasks in failure
 notification.dispatcher.description_x=Check to receive notification for {0}
+notification.calculation_change.message.TRK=The way we calculate ratings has changed and it might affect your security, reliability, maintainability and security review ratings. {link}
+notification.calculation_change.message.VW=The way we calculate ratings has changed and it might affect your releasability, security, reliability, maintainability and security review ratings. {link}
 
 #------------------------------------------------------------------------------
 #
@@ -2725,7 +2727,6 @@ alert.tooltip.info=This is an info message.
 
 alert.dismiss=Dismiss this message
 
-
 #------------------------------------------------------------------------------
 #
 # USER