]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13075 Show a legend for the New Code period on the activity graph
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Mon, 15 Jun 2020 15:46:06 +0000 (17:46 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 19 Jun 2020 20:04:42 +0000 (20:04 +0000)
14 files changed:
server/sonar-web/src/main/js/apps/overview/styles.css
server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsLegendCustom.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsLegendNewCode.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/GraphsLegendStatic.tsx
server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendCustom-test.tsx
server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendNewCode-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendStatic-test.tsx
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphHistory-test.tsx.snap
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendCustom-test.tsx.snap
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendNewCode-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendStatic-test.tsx.snap
server/sonar-web/src/main/js/components/activity-graph/styles.css
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index d07c641e950b192dc80a0573a08b76ec139000de..ccdaeb8c740144afd14d4c59596b186511da5e6f 100644 (file)
   margin-top: -30px;
 }
 
+.overview-panel .activity-graph-new-code-legend {
+  position: relative;
+  z-index: var(--aboveNormalZIndex);
+  width: 12px;
+  overflow: hidden;
+  margin-top: 1px;
+  margin-left: calc(2 * var(--gridSize));
+  text-indent: -9999px;
+}
+
+.overview-panel .activity-graph-new-code-legend::after {
+  margin: 0;
+}
+
 .overview-analysis {
   color: var(--secondFontColor);
 }
index da94b4e0a0821eb3b1cc5870727ded729a802534..366cc3aac76ede2801cec7aeb9cc4a04775894e0 100644 (file)
@@ -85,10 +85,15 @@ export default class GraphHistory extends React.PureComponent<Props, State> {
     return (
       <div className="activity-graph-container flex-grow display-flex-column display-flex-stretch display-flex-justify-center">
         {isCustom && this.props.removeCustomMetric ? (
-          <GraphsLegendCustom removeMetric={this.props.removeCustomMetric} series={series} />
+          <GraphsLegendCustom
+            removeMetric={this.props.removeCustomMetric}
+            series={series}
+            showLeakLegend={Boolean(leakPeriodDate)}
+          />
         ) : (
-          <GraphsLegendStatic series={series} />
+          <GraphsLegendStatic series={series} showLeakLegend={Boolean(leakPeriodDate)} />
         )}
+
         <div className="flex-1">
           <AutoSizer>
             {({ height, width }) => (
index aaf7157335181793faeb470e36752f0d975a830c..0e120067944b114284a076cf36dd970079449d7d 100644 (file)
@@ -22,42 +22,49 @@ import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
 import { translate } from 'sonar-ui-common/helpers/l10n';
 import { Serie } from '../../types/project-activity';
 import GraphsLegendItem from './GraphsLegendItem';
+import GraphsLegendNewCode from './GraphsLegendNewCode';
 import { hasDataValues } from './utils';
 
-interface Props {
+export interface GraphsLegendCustomProps {
   removeMetric: (metric: string) => void;
   series: Serie[];
+  showLeakLegend: boolean;
 }
 
-export default function GraphsLegendCustom({ removeMetric, series }: Props) {
+export default function GraphsLegendCustom(props: GraphsLegendCustomProps) {
+  const { series, showLeakLegend } = props;
   return (
-    <div className="activity-graph-legends">
-      {series.map((serie, idx) => {
-        const hasData = hasDataValues(serie);
-        const legendItem = (
-          <GraphsLegendItem
-            index={idx}
-            metric={serie.name}
-            name={serie.translatedName}
-            removeMetric={removeMetric}
-            showWarning={!hasData}
-          />
-        );
-        if (!hasData) {
+    <div className="activity-graph-legends display-flex-center">
+      <div className="flex-1">
+        {series.map((serie, idx) => {
+          const hasData = hasDataValues(serie);
+          const legendItem = (
+            <GraphsLegendItem
+              index={idx}
+              metric={serie.name}
+              name={serie.translatedName}
+              removeMetric={props.removeMetric}
+              showWarning={!hasData}
+            />
+          );
+          if (!hasData) {
+            return (
+              <Tooltip
+                key={serie.name}
+                overlay={translate('project_activity.graphs.custom.metric_no_history')}>
+                <span className="spacer-left spacer-right">{legendItem}</span>
+              </Tooltip>
+            );
+          }
           return (
-            <Tooltip
-              key={serie.name}
-              overlay={translate('project_activity.graphs.custom.metric_no_history')}>
-              <span className="spacer-left spacer-right">{legendItem}</span>
-            </Tooltip>
+            <span className="spacer-left spacer-right" key={serie.name}>
+              {legendItem}
+            </span>
           );
-        }
-        return (
-          <span className="spacer-left spacer-right" key={serie.name}>
-            {legendItem}
-          </span>
-        );
-      })}
+        })}
+      </div>
+
+      {showLeakLegend && <GraphsLegendNewCode />}
     </div>
   );
 }
diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendNewCode.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendNewCode.tsx
new file mode 100644 (file)
index 0000000..d128f68
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 Tooltip from 'sonar-ui-common/components/controls/Tooltip';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+
+export default function GraphsLegendNewCode() {
+  return (
+    <Tooltip overlay={translate('project_activity.graphs.new_code_long')}>
+      <span
+        aria-label={translate('project_activity.graphs.new_code_long')}
+        className="activity-graph-new-code-legend display-flex-center pull-right note">
+        {translate('project_activity.graphs.new_code')}
+      </span>
+    </Tooltip>
+  );
+}
index 9d723ad7071936bf9e54e361638d00cb71e81769..54349d2a99d39b56bd16a132e7fb83411e8cc8a1 100644 (file)
 import * as React from 'react';
 import { Serie } from '../../types/project-activity';
 import GraphsLegendItem from './GraphsLegendItem';
+import GraphsLegendNewCode from './GraphsLegendNewCode';
 
-interface Props {
+export interface GraphsLegendStaticProps {
   series: Array<Pick<Serie, 'name' | 'translatedName'>>;
+  showLeakLegend: boolean;
 }
 
-export default function GraphsLegendStatic({ series }: Props) {
+export default function GraphsLegendStatic({ series, showLeakLegend }: GraphsLegendStaticProps) {
   return (
     <div className="activity-graph-legends">
       {series.map((serie, idx) => (
@@ -37,6 +39,8 @@ export default function GraphsLegendStatic({ series }: Props) {
           name={serie.translatedName}
         />
       ))}
+
+      {showLeakLegend && <GraphsLegendNewCode />}
     </div>
   );
 }
index 5b213e8141f57a67900e42431fe938d1d0f6cc8f..9d437a7a0f1b10d5f514f895d5edfe6fc19ad708 100644 (file)
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { parseDate } from 'sonar-ui-common/helpers/dates';
-import GraphsLegendCustom from '../GraphsLegendCustom';
+import GraphsLegendCustom, { GraphsLegendCustomProps } from '../GraphsLegendCustom';
 
-const SERIES = [
-  {
-    name: 'bugs',
-    translatedName: 'Bugs',
-    data: [{ x: parseDate('2017-05-16T13:50:02+0200'), y: 1 }],
-    type: 'INT'
-  },
-  {
-    name: 'my_metric',
-    translatedName: 'My Metric',
-    data: [{ x: parseDate('2017-05-16T13:50:02+0200'), y: 1 }],
-    type: 'INT'
-  },
-  {
-    name: 'foo',
-    translatedName: 'Foo',
-    data: [],
-    type: 'INT'
-  }
-];
-
-it('should render correctly the list of series', () => {
-  expect(shallow(<GraphsLegendCustom removeMetric={() => {}} series={SERIES} />)).toMatchSnapshot();
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+  expect(shallowRender({ showLeakLegend: true })).toMatchSnapshot('with leak legend');
 });
+
+function shallowRender(props: Partial<GraphsLegendCustomProps> = {}) {
+  return shallow<GraphsLegendCustomProps>(
+    <GraphsLegendCustom
+      removeMetric={jest.fn()}
+      series={[
+        {
+          name: 'bugs',
+          translatedName: 'Bugs',
+          data: [{ x: parseDate('2017-05-16T13:50:02+0200'), y: 1 }],
+          type: 'INT'
+        },
+        {
+          name: 'my_metric',
+          translatedName: 'My Metric',
+          data: [{ x: parseDate('2017-05-16T13:50:02+0200'), y: 1 }],
+          type: 'INT'
+        },
+        {
+          name: 'foo',
+          translatedName: 'Foo',
+          data: [],
+          type: 'INT'
+        }
+      ]}
+      showLeakLegend={false}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendNewCode-test.tsx b/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendNewCode-test.tsx
new file mode 100644 (file)
index 0000000..7f74bf2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { shallow } from 'enzyme';
+import * as React from 'react';
+import GraphsLegendNewCode from '../GraphsLegendNewCode';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+});
+
+function shallowRender() {
+  return shallow(<GraphsLegendNewCode />);
+}
index a0492576ddf231eda8194c28624a548e66e09e2e..4fa8d55ed7a1b0c27ab5184553943cce586961ae 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import GraphsLegendStatic from '../GraphsLegendStatic';
+import GraphsLegendStatic, { GraphsLegendStaticProps } from '../GraphsLegendStatic';
 
-const SERIES = [
-  { name: 'bugs', translatedName: 'Bugs', data: [] },
-  { name: 'code_smells', translatedName: 'Code Smells', data: [] }
-];
-
-it('should render correctly the list of series', () => {
-  expect(shallow(<GraphsLegendStatic series={SERIES} />)).toMatchSnapshot();
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+  expect(shallowRender({ showLeakLegend: true })).toMatchSnapshot('with leak legend');
 });
+
+function shallowRender(props: Partial<GraphsLegendStaticProps> = {}) {
+  return shallow<GraphsLegendStaticProps>(
+    <GraphsLegendStatic
+      series={[
+        { name: 'bugs', translatedName: 'Bugs' },
+        { name: 'code_smells', translatedName: 'Code Smells' }
+      ]}
+      showLeakLegend={false}
+      {...props}
+    />
+  );
+}
index cb92ba1524de99fb66a1ad039a3841e0bc6a1246..d3f064bbe89abe49cf0bfa6e0f01479f7e2ae975 100644 (file)
@@ -28,6 +28,7 @@ exports[`should correctly render a graph 1`] = `
         },
       ]
     }
+    showLeakLegend={true}
   />
   <div
     className="flex-1"
index 9dede77b48a1330fb19a9d308637139f44ae6883..35be613cd2d75803156f96492cf0a0befde2f1ce 100644 (file)
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should render correctly the list of series 1`] = `
+exports[`should render correctly: default 1`] = `
 <div
-  className="activity-graph-legends"
+  className="activity-graph-legends display-flex-center"
 >
-  <span
-    className="spacer-left spacer-right"
-    key="bugs"
+  <div
+    className="flex-1"
   >
-    <GraphsLegendItem
-      index={0}
-      metric="bugs"
-      name="Bugs"
-      removeMetric={[Function]}
-      showWarning={false}
-    />
-  </span>
-  <span
-    className="spacer-left spacer-right"
-    key="my_metric"
-  >
-    <GraphsLegendItem
-      index={1}
-      metric="my_metric"
-      name="My Metric"
-      removeMetric={[Function]}
-      showWarning={false}
-    />
-  </span>
-  <Tooltip
-    key="foo"
-    overlay="project_activity.graphs.custom.metric_no_history"
+    <span
+      className="spacer-left spacer-right"
+      key="bugs"
+    >
+      <GraphsLegendItem
+        index={0}
+        metric="bugs"
+        name="Bugs"
+        removeMetric={[MockFunction]}
+        showWarning={false}
+      />
+    </span>
+    <span
+      className="spacer-left spacer-right"
+      key="my_metric"
+    >
+      <GraphsLegendItem
+        index={1}
+        metric="my_metric"
+        name="My Metric"
+        removeMetric={[MockFunction]}
+        showWarning={false}
+      />
+    </span>
+    <Tooltip
+      key="foo"
+      overlay="project_activity.graphs.custom.metric_no_history"
+    >
+      <span
+        className="spacer-left spacer-right"
+      >
+        <GraphsLegendItem
+          index={2}
+          metric="foo"
+          name="Foo"
+          removeMetric={[MockFunction]}
+          showWarning={true}
+        />
+      </span>
+    </Tooltip>
+  </div>
+</div>
+`;
+
+exports[`should render correctly: with leak legend 1`] = `
+<div
+  className="activity-graph-legends display-flex-center"
+>
+  <div
+    className="flex-1"
   >
     <span
       className="spacer-left spacer-right"
+      key="bugs"
     >
       <GraphsLegendItem
-        index={2}
-        metric="foo"
-        name="Foo"
-        removeMetric={[Function]}
-        showWarning={true}
+        index={0}
+        metric="bugs"
+        name="Bugs"
+        removeMetric={[MockFunction]}
+        showWarning={false}
       />
     </span>
-  </Tooltip>
+    <span
+      className="spacer-left spacer-right"
+      key="my_metric"
+    >
+      <GraphsLegendItem
+        index={1}
+        metric="my_metric"
+        name="My Metric"
+        removeMetric={[MockFunction]}
+        showWarning={false}
+      />
+    </span>
+    <Tooltip
+      key="foo"
+      overlay="project_activity.graphs.custom.metric_no_history"
+    >
+      <span
+        className="spacer-left spacer-right"
+      >
+        <GraphsLegendItem
+          index={2}
+          metric="foo"
+          name="Foo"
+          removeMetric={[MockFunction]}
+          showWarning={true}
+        />
+      </span>
+    </Tooltip>
+  </div>
+  <GraphsLegendNewCode />
 </div>
 `;
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendNewCode-test.tsx.snap b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendNewCode-test.tsx.snap
new file mode 100644 (file)
index 0000000..e6c7707
--- /dev/null
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<Tooltip
+  overlay="project_activity.graphs.new_code_long"
+>
+  <span
+    aria-label="project_activity.graphs.new_code_long"
+    className="activity-graph-new-code-legend display-flex-center pull-right note"
+  >
+    project_activity.graphs.new_code
+  </span>
+</Tooltip>
+`;
index d1198d5f498a93626a54791b49c694040136a633..cb0be89ef40af3dc0b43045e7c4854aebc8c37b8 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should render correctly the list of series 1`] = `
+exports[`should render correctly: default 1`] = `
 <div
   className="activity-graph-legends"
 >
@@ -20,3 +20,25 @@ exports[`should render correctly the list of series 1`] = `
   />
 </div>
 `;
+
+exports[`should render correctly: with leak legend 1`] = `
+<div
+  className="activity-graph-legends"
+>
+  <GraphsLegendItem
+    className="big-spacer-left big-spacer-right"
+    index={0}
+    key="bugs"
+    metric="bugs"
+    name="Bugs"
+  />
+  <GraphsLegendItem
+    className="big-spacer-left big-spacer-right"
+    index={1}
+    key="code_smells"
+    metric="code_smells"
+    name="Code Smells"
+  />
+  <GraphsLegendNewCode />
+</div>
+`;
index 277d3084262329239936ee31a4295c4e3fdc1e8d..61d2759ce79856f678fb21bb8ad581db602835a8 100644 (file)
   border-style: solid;
   border-radius: calc(1.5 * var(--gridSize));
 }
+
+.activity-graph-new-code-legend {
+  margin-right: 10px; /* padding of activity graph */
+}
+
+.activity-graph-new-code-legend::after {
+  content: '';
+  display: inline-block;
+  margin-left: calc(var(--gridSize) / 2);
+  width: var(--gridSize);
+  height: var(--gridSize);
+  background-color: var(--leakPrimaryColor);
+  border: 2px solid var(--leakSecondaryColor);
+}
index bb781d111baeec7a01296e541d5ebe4c0b4762c2..789b8cf67183b0fedfa945d45c9f73da645c7769 100644 (file)
@@ -1301,6 +1301,8 @@ project_activity.events.tooltip.delete=Delete this event
 project_activity.new_code_period_start=New Code Period starts here
 project_activity.new_code_period_start.help=The analysis before this mark is the baseline for New Code comparison
 
+project_activity.graphs.new_code=New Code
+project_activity.graphs.new_code_long=New Code is indicated in yellow on the graph.
 project_activity.graphs.issues=Issues
 project_activity.graphs.coverage=Coverage
 project_activity.graphs.duplications=Duplications