diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2020-06-15 17:46:06 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-06-19 20:04:42 +0000 |
commit | ae807c088ed8910660af642766692feb62bfdab0 (patch) | |
tree | 755860c9fd36303babc24b41cb411144e981e28a | |
parent | 72a34678a0da474c17e1d375a7e9f8699f9433ce (diff) | |
download | sonarqube-ae807c088ed8910660af642766692feb62bfdab0.tar.gz sonarqube-ae807c088ed8910660af642766692feb62bfdab0.zip |
SONAR-13075 Show a legend for the New Code period on the activity graph
14 files changed, 321 insertions, 98 deletions
diff --git a/server/sonar-web/src/main/js/apps/overview/styles.css b/server/sonar-web/src/main/js/apps/overview/styles.css index d07c641e950..ccdaeb8c740 100644 --- a/server/sonar-web/src/main/js/apps/overview/styles.css +++ b/server/sonar-web/src/main/js/apps/overview/styles.css @@ -272,6 +272,20 @@ 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); } diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx index da94b4e0a08..366cc3aac76 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx @@ -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 }) => ( diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendCustom.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendCustom.tsx index aaf71573351..0e120067944 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendCustom.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendCustom.tsx @@ -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 index 00000000000..d128f685a74 --- /dev/null +++ b/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendNewCode.tsx @@ -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> + ); +} diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendStatic.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendStatic.tsx index 9d723ad7071..54349d2a99d 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendStatic.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/GraphsLegendStatic.tsx @@ -20,12 +20,14 @@ 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> ); } diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendCustom-test.tsx b/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendCustom-test.tsx index 5b213e8141f..9d437a7a0f1 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendCustom-test.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendCustom-test.tsx @@ -20,29 +20,39 @@ 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 index 00000000000..7f74bf22117 --- /dev/null +++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendNewCode-test.tsx @@ -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 />); +} diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendStatic-test.tsx b/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendStatic-test.tsx index a0492576ddf..4fa8d55ed7a 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendStatic-test.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphsLegendStatic-test.tsx @@ -19,13 +19,22 @@ */ 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} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphHistory-test.tsx.snap b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphHistory-test.tsx.snap index cb92ba1524d..d3f064bbe89 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphHistory-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphHistory-test.tsx.snap @@ -28,6 +28,7 @@ exports[`should correctly render a graph 1`] = ` }, ] } + showLeakLegend={true} /> <div className="flex-1" diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendCustom-test.tsx.snap b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendCustom-test.tsx.snap index 9dede77b48a..35be613cd2d 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendCustom-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendCustom-test.tsx.snap @@ -1,48 +1,104 @@ // 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 index 00000000000..e6c7707ef39 --- /dev/null +++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendNewCode-test.tsx.snap @@ -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> +`; diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendStatic-test.tsx.snap b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendStatic-test.tsx.snap index d1198d5f498..cb0be89ef40 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendStatic-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphsLegendStatic-test.tsx.snap @@ -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> +`; diff --git a/server/sonar-web/src/main/js/components/activity-graph/styles.css b/server/sonar-web/src/main/js/components/activity-graph/styles.css index 277d3084262..61d2759ce79 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/styles.css +++ b/server/sonar-web/src/main/js/components/activity-graph/styles.css @@ -67,3 +67,17 @@ 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); +} 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 bb781d111ba..789b8cf6718 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -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 |