]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11616 Save selected activity graph per project
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Tue, 8 Jan 2019 13:11:32 +0000 (14:11 +0100)
committerSonarTech <sonartech@sonarsource.com>
Wed, 9 Jan 2019 19:21:07 +0000 (20:21 +0100)
server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityGraphs-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap
server/sonar-web/src/main/js/apps/projectActivity/utils.ts
server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.tsx

index 6bf32b4b15997e3ec02cb20a43c8fb7564f14386..4a7c46e37545381754efe724829f2169505bf3fa 100644 (file)
@@ -33,13 +33,11 @@ import { getAllTimeMachineData } from '../../../api/time-machine';
 import { parseDate } from '../../../helpers/dates';
 import { enhanceMeasuresWithMetrics } from '../../../helpers/measures';
 import { getLeakPeriod } from '../../../helpers/periods';
-import { get } from '../../../helpers/storage';
 import { METRICS, HISTORY_METRICS_LIST } from '../utils';
 import {
   DEFAULT_GRAPH,
   getDisplayedHistoryMetrics,
-  PROJECT_ACTIVITY_GRAPH,
-  PROJECT_ACTIVITY_GRAPH_CUSTOM
+  getProjectActivityGraph
 } from '../../projectActivity/utils';
 import {
   isSameBranchLike,
@@ -51,22 +49,14 @@ import { getMetrics, Store } from '../../../store/rootReducer';
 import { translate } from '../../../helpers/l10n';
 import '../styles.css';
 
-interface OwnProps {
+interface Props {
   branchLike?: T.BranchLike;
   component: T.Component;
+  fetchMetrics: () => void;
   onComponentChange: (changes: {}) => void;
-}
-
-interface StateToProps {
   metrics: { [key: string]: T.Metric };
 }
 
-interface DispatchToProps {
-  fetchMetrics: () => void;
-}
-
-type Props = StateToProps & DispatchToProps & OwnProps;
-
 interface State {
   history?: {
     [metric: string]: Array<{ date: Date; value?: string }>;
@@ -100,40 +90,24 @@ export class OverviewApp extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  loadMeasures() {
-    const { branchLike, component } = this.props;
-    this.setState({ loading: true });
+  getApplicationLeakPeriod = () => {
+    return this.state.measures.find(measure => measure.metric.key === 'new_bugs')
+      ? ({ index: 1 } as T.Period)
+      : undefined;
+  };
 
-    return getMeasuresAndMeta(component.key, METRICS, {
-      additionalFields: 'metrics,periods',
-      ...getBranchLikeQuery(branchLike)
-    }).then(
-      ({ component, metrics, periods }) => {
-        if (this.mounted && metrics && component.measures) {
-          this.setState({
-            loading: false,
-            measures: enhanceMeasuresWithMetrics(component.measures, metrics),
-            periods
-          });
-        }
-      },
-      error => {
-        throwGlobalError(error);
-        if (this.mounted) {
-          this.setState({ loading: false });
-        }
-      }
+  isEmpty = () => {
+    return (
+      this.state.measures === undefined ||
+      this.state.measures.find(measure => measure.metric.key === 'ncloc') === undefined
     );
-  }
+  };
 
   loadHistory = () => {
     const { branchLike, component } = this.props;
 
-    const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
-    let graphMetrics = getDisplayedHistoryMetrics(
-      get(PROJECT_ACTIVITY_GRAPH) || 'issues',
-      customGraphs ? customGraphs.split(',') : []
-    );
+    const { graph, customGraphs } = getProjectActivityGraph(component.key);
+    let graphMetrics = getDisplayedHistoryMetrics(graph, customGraphs);
     if (!graphMetrics || graphMetrics.length <= 0) {
       graphMetrics = getDisplayedHistoryMetrics(DEFAULT_GRAPH, []);
     }
@@ -159,24 +133,33 @@ export class OverviewApp extends React.PureComponent<Props, State> {
     });
   };
 
-  getApplicationLeakPeriod = () =>
-    this.state.measures.find(measure => measure.metric.key === 'new_bugs')
-      ? ({ index: 1 } as T.Period)
-      : undefined;
-
-  isEmpty = () =>
-    this.state.measures === undefined ||
-    this.state.measures.find(measure => measure.metric.key === 'ncloc') === undefined;
+  loadMeasures = () => {
+    const { branchLike, component } = this.props;
+    this.setState({ loading: true });
 
-  renderLoading() {
-    return (
-      <div className="text-center">
-        <i className="spinner spinner-margin" />
-      </div>
+    return getMeasuresAndMeta(component.key, METRICS, {
+      additionalFields: 'metrics,periods',
+      ...getBranchLikeQuery(branchLike)
+    }).then(
+      ({ component, metrics, periods }) => {
+        if (this.mounted && metrics && component.measures) {
+          this.setState({
+            loading: false,
+            measures: enhanceMeasuresWithMetrics(component.measures, metrics),
+            periods
+          });
+        }
+      },
+      error => {
+        throwGlobalError(error);
+        if (this.mounted) {
+          this.setState({ loading: false });
+        }
+      }
     );
-  }
+  };
 
-  renderEmpty() {
+  renderEmpty = () => {
     const { component } = this.props;
     const isApp = component.qualifier === 'APP';
     return (
@@ -191,9 +174,17 @@ export class OverviewApp extends React.PureComponent<Props, State> {
         </h3>
       </div>
     );
-  }
+  };
 
-  renderMain() {
+  renderLoading = () => {
+    return (
+      <div className="text-center">
+        <i className="spinner spinner-margin" />
+      </div>
+    );
+  };
+
+  renderMain = () => {
     const { branchLike, component } = this.props;
     const { periods, measures, history, historyStartDate } = this.state;
     const leakPeriod =
@@ -230,7 +221,7 @@ export class OverviewApp extends React.PureComponent<Props, State> {
         </div>
       </div>
     );
-  }
+  };
 
   render() {
     const { branchLike, component } = this.props;
@@ -261,13 +252,9 @@ export class OverviewApp extends React.PureComponent<Props, State> {
   }
 }
 
-const mapDispatchToProps: DispatchToProps = {
-  fetchMetrics
-};
+const mapDispatchToProps = { fetchMetrics };
 
-const mapStateToProps = (state: Store): StateToProps => ({
-  metrics: getMetrics(state)
-});
+const mapStateToProps = (state: Store) => ({ metrics: getMetrics(state) });
 
 export default connect(
   mapStateToProps,
index d479b23a8b74d8991cc152a159975b10cf21d237..93a9f377231514bad7fdaaa7e7cc5d7cc1f708e7 100644 (file)
@@ -21,14 +21,12 @@ import * as React from 'react';
 import {
   getDisplayedHistoryMetrics,
   DEFAULT_GRAPH,
-  PROJECT_ACTIVITY_GRAPH,
-  PROJECT_ACTIVITY_GRAPH_CUSTOM
+  getProjectActivityGraph
 } from '../../projectActivity/utils';
 import PreviewGraph from '../../../components/preview-graph/PreviewGraph';
 import { getAllTimeMachineData } from '../../../api/time-machine';
 import { parseDate } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
-import { get } from '../../../helpers/storage';
 
 interface Props {
   component: string;
@@ -64,11 +62,8 @@ export default class Activity extends React.PureComponent<Props> {
   fetchHistory = () => {
     const { component } = this.props;
 
-    const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
-    let graphMetrics = getDisplayedHistoryMetrics(
-      get(PROJECT_ACTIVITY_GRAPH) || 'issues',
-      customGraphs ? customGraphs.split(',') : []
-    );
+    const { graph, customGraphs } = getProjectActivityGraph(component);
+    let graphMetrics = getDisplayedHistoryMetrics(graph, customGraphs);
     if (!graphMetrics || graphMetrics.length <= 0) {
       graphMetrics = getDisplayedHistoryMetrics(DEFAULT_GRAPH, []);
     }
index 0b24e2881ca375c49845b8e2956edbf64eb653e1..d7a67e3c27523a904beb1a819f59228da19a84af 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-/* eslint-disable import/first, import/order */
-jest.mock('../../../../helpers/storage', () => ({
-  get: (key: string) => (key === 'sonarqube.project_activity.graph.custom' ? 'coverage' : 'custom')
-}));
-
-jest.mock('../../../../api/time-machine', () => ({
-  getAllTimeMachineData: jest.fn(() =>
-    Promise.resolve({
-      measures: [
-        {
-          metric: 'coverage',
-          history: [
-            { date: '2017-01-01T00:00:00.000Z', value: '73' },
-            { date: '2017-01-02T00:00:00.000Z', value: '82' }
-          ]
-        }
-      ]
-    })
-  )
-}));
-
 import * as React from 'react';
 import { mount, shallow } from 'enzyme';
 import Activity from '../Activity';
+import { getAllTimeMachineData } from '../../../../api/time-machine';
+import { getProjectActivityGraph } from '../../../projectActivity/utils';
+
+jest.mock('../../../projectActivity/utils', () => {
+  const utils = require.requireActual('../../../projectActivity/utils');
+  utils.getProjectActivityGraph = jest
+    .fn()
+    .mockReturnValue({ graph: 'custom', customGraphs: ['coverage'] });
+  return utils;
+});
 
-const getAllTimeMachineData = require('../../../../api/time-machine')
-  .getAllTimeMachineData as jest.Mock<any>;
+jest.mock('../../../../api/time-machine', () => ({
+  getAllTimeMachineData: jest.fn().mockResolvedValue({
+    measures: [
+      {
+        metric: 'coverage',
+        history: [
+          { date: '2017-01-01T00:00:00.000Z', value: '73' },
+          { date: '2017-01-02T00:00:00.000Z', value: '82' }
+        ]
+      }
+    ]
+  })
+}));
 
 beforeEach(() => {
-  getAllTimeMachineData.mockClear();
+  (getAllTimeMachineData as jest.Mock).mockClear();
+  (getProjectActivityGraph as jest.Mock).mockClear();
 });
 
 it('renders', () => {
@@ -62,6 +63,7 @@ it('renders', () => {
     metrics: [{ key: 'coverage' }]
   });
   expect(wrapper).toMatchSnapshot();
+  expect(getProjectActivityGraph).toBeCalledWith('foo');
 });
 
 it('fetches history', () => {
index 19ef76c801be6949e7b4ad79e05a7500f026be8c..4a76f2491b2a55fec8a840c1c6dab357c3b0ec99 100644 (file)
@@ -38,7 +38,7 @@ interface Props {
   deleteEvent: (analysis: string, event: string) => Promise<void>;
   graphLoading: boolean;
   initializing: boolean;
-  project: Pick<T.Component, 'configuration' | 'leakPeriodDate' | 'qualifier'>;
+  project: Pick<T.Component, 'configuration' | 'key' | 'leakPeriodDate' | 'qualifier'>;
   metrics: T.Metric[];
   measuresHistory: MeasureHistory[];
   query: Query;
@@ -93,6 +93,7 @@ export default function ProjectActivityApp(props: Props) {
             loading={props.graphLoading}
             measuresHistory={measuresHistory}
             metrics={props.metrics}
+            project={props.project.key}
             query={query}
             updateQuery={props.updateQuery}
           />
index 62eab86aea4b2109add0b132ffa83b7f88bcf46a..fcffaf0c37399ae120746f766b268312546f803e 100644 (file)
@@ -27,20 +27,18 @@ import * as api from '../../../api/projectActivity';
 import * as actions from '../actions';
 import { getBranchLikeQuery } from '../../../helpers/branches';
 import { parseDate } from '../../../helpers/dates';
-import { get } from '../../../helpers/storage';
 import {
   customMetricsChanged,
   DEFAULT_GRAPH,
   getHistoryMetrics,
+  getProjectActivityGraph,
   isCustomGraph,
-  parseQuery,
-  PROJECT_ACTIVITY_GRAPH,
-  PROJECT_ACTIVITY_GRAPH_CUSTOM,
-  serializeQuery,
-  serializeUrlQuery,
   MeasureHistory,
+  parseQuery,
+  ParsedAnalysis,
   Query,
-  ParsedAnalysis
+  serializeQuery,
+  serializeUrlQuery
 } from '../utils';
 import { RawQuery } from '../../../helpers/query';
 
@@ -81,10 +79,10 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
   componentDidMount() {
     this.mounted = true;
     if (this.shouldRedirect()) {
-      const newQuery = { ...this.state.query, graph: get(PROJECT_ACTIVITY_GRAPH) || 'issues' };
+      const { graph, customGraphs } = getProjectActivityGraph(this.props.component.key);
+      const newQuery = { ...this.state.query, graph };
       if (isCustomGraph(newQuery.graph)) {
-        const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
-        newQuery.customMetrics = customGraphs ? customGraphs.split(',') : [];
+        newQuery.customMetrics = customGraphs;
       }
       this.props.router.replace({
         pathname: this.props.location.pathname,
@@ -98,14 +96,14 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
     }
   }
 
-  componentWillReceiveProps(nextProps: Props) {
-    if (nextProps.location.query !== this.props.location.query) {
-      const query = parseQuery(nextProps.location.query);
+  componentDidUpdate(prevProps: Props) {
+    if (prevProps.location.query !== this.props.location.query) {
+      const query = parseQuery(this.props.location.query);
       if (query.graph !== this.state.query.graph || customMetricsChanged(this.state.query, query)) {
         if (this.state.initialized) {
           this.updateGraphData(query.graph, query.customMetrics);
         } else {
-          this.firstLoadData(query, nextProps.component);
+          this.firstLoadData(query, this.props.component);
         }
       }
       this.setState({ query });
@@ -185,6 +183,26 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
     );
   };
 
+  fetchAllActivities = (topLevelComponent: string) => {
+    this.setState({ analysesLoading: true });
+    this.loadAllActivities(topLevelComponent).then(
+      ({ analyses, paging }) => {
+        if (this.mounted) {
+          this.setState({
+            analyses,
+            analysesLoading: false,
+            paging
+          });
+        }
+      },
+      () => {
+        if (this.mounted) {
+          this.setState({ analysesLoading: false });
+        }
+      }
+    );
+  };
+
   loadAllActivities = (
     project: string,
     prevResult?: { analyses: ParsedAnalysis[]; paging: T.Paging }
@@ -230,7 +248,6 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
         if (this.mounted) {
           this.setState({
             analyses: response[0].analyses,
-            analysesLoading: true,
             graphLoading: false,
             initialized: true,
             measuresHistory: response[2],
@@ -238,20 +255,12 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
             paging: response[0].paging
           });
 
-          this.loadAllActivities(topLevelComponent).then(({ analyses, paging }) => {
-            if (this.mounted) {
-              this.setState({
-                analyses,
-                analysesLoading: false,
-                paging
-              });
-            }
-          });
+          this.fetchAllActivities(topLevelComponent);
         }
       },
       () => {
         if (this.mounted) {
-          this.setState({ initialized: true, analysesLoading: false, graphLoading: false });
+          this.setState({ initialized: true, graphLoading: false });
         }
       }
     );
@@ -298,10 +307,8 @@ export default class ProjectActivityAppContainer extends React.PureComponent<Pro
       key => key !== 'id' && locationQuery[key] !== ''
     );
 
-    const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
-    const graph = get(PROJECT_ACTIVITY_GRAPH) || 'issues';
-    const emptyCustomGraph =
-      isCustomGraph(graph) && customGraphs && customGraphs.split(',').length <= 0;
+    const { graph, customGraphs } = getProjectActivityGraph(this.props.component.key);
+    const emptyCustomGraph = isCustomGraph(graph) && customGraphs.length <= 0;
 
     // if there is no filter, but there are saved preferences in the localStorage
     // also don't redirect to custom if there is no metrics selected for it
index 86ee0413791eb51650dd4ca205386468ed66201e..1e5497f977963824fdbdf2091bd2a73902fb3313 100644 (file)
@@ -22,7 +22,7 @@ import { debounce, findLast, maxBy, minBy, sortBy } from 'lodash';
 import ProjectActivityGraphsHeader from './ProjectActivityGraphsHeader';
 import GraphsZoom from './GraphsZoom';
 import GraphsHistory from './GraphsHistory';
-import { get, save } from '../../../helpers/storage';
+import { save } from '../../../helpers/storage';
 import {
   datesQueryChanged,
   generateSeries,
@@ -30,14 +30,15 @@ import {
   getSeriesMetricType,
   historyQueryChanged,
   isCustomGraph,
+  MeasureHistory,
+  ParsedAnalysis,
+  Point,
   PROJECT_ACTIVITY_GRAPH,
   PROJECT_ACTIVITY_GRAPH_CUSTOM,
-  splitSeriesInGraphs,
-  MeasureHistory,
   Query,
   Serie,
-  Point,
-  ParsedAnalysis
+  splitSeriesInGraphs,
+  getProjectActivityGraph
 } from '../utils';
 
 interface Props {
@@ -46,6 +47,7 @@ interface Props {
   loading: boolean;
   measuresHistory: MeasureHistory[];
   metrics: T.Metric[];
+  project: string;
   query: Query;
   updateQuery: (changes: Partial<Query>) => void;
 }
@@ -77,23 +79,23 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St
     this.updateQueryDateRange = debounce(this.updateQueryDateRange, 500);
   }
 
-  componentWillReceiveProps(nextProps: Props) {
+  componentDidUpdate(prevProps: Props) {
     let newSeries;
     let newGraphs;
     if (
-      nextProps.measuresHistory !== this.props.measuresHistory ||
-      historyQueryChanged(this.props.query, nextProps.query)
+      prevProps.measuresHistory !== this.props.measuresHistory ||
+      historyQueryChanged(prevProps.query, this.props.query)
     ) {
       newSeries = generateSeries(
-        nextProps.measuresHistory,
-        nextProps.query.graph,
-        nextProps.metrics,
-        getDisplayedHistoryMetrics(nextProps.query.graph, nextProps.query.customMetrics)
+        this.props.measuresHistory,
+        this.props.query.graph,
+        this.props.metrics,
+        getDisplayedHistoryMetrics(this.props.query.graph, this.props.query.customMetrics)
       );
       newGraphs = splitSeriesInGraphs(newSeries, MAX_GRAPH_NB, MAX_SERIES_PER_GRAPH);
     }
 
-    const newDates = this.getStateZoomDates(this.props, nextProps, newSeries);
+    const newDates = this.getStateZoomDates(prevProps, this.props, newSeries);
 
     if (newSeries || newDates) {
       let newState = {} as State;
@@ -110,28 +112,27 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St
     }
   }
 
-  getStateZoomDates = (props: Props | undefined, nextProps: Props, newSeries?: Serie[]) => {
+  getStateZoomDates = (prevProps: Props | undefined, props: Props, newSeries?: Serie[]) => {
     const newDates = {
-      from: nextProps.query.from || undefined,
-      to: nextProps.query.to || undefined
+      from: props.query.from || undefined,
+      to: props.query.to || undefined
     };
-    if (!props || datesQueryChanged(props.query, nextProps.query)) {
+    if (!prevProps || datesQueryChanged(prevProps.query, props.query)) {
       return { graphEndDate: newDates.to, graphStartDate: newDates.from };
     }
 
     if (newDates.to === undefined && newDates.from === undefined && newSeries !== undefined) {
-      const series = newSeries ? newSeries : this.state.series;
       const firstValid = minBy(
-        series.map(serie => serie.data.find(p => Boolean(p.y || p.y === 0))),
+        newSeries.map(serie => serie.data.find(p => Boolean(p.y || p.y === 0))),
         'x'
       );
       const lastValid = maxBy<Point>(
-        series.map(serie => findLast(serie.data, p => Boolean(p.y || p.y === 0))!),
+        newSeries.map(serie => findLast(serie.data, p => Boolean(p.y || p.y === 0))!),
         'x'
       );
       return {
-        graphEndDate: lastValid ? lastValid.x : newDates.to,
-        graphStartDate: firstValid ? firstValid.x : newDates.from
+        graphEndDate: lastValid && lastValid.x,
+        graphStartDate: firstValid && firstValid.x
       };
     }
     return null;
@@ -148,21 +149,21 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St
 
   addCustomMetric = (metric: string) => {
     const customMetrics = [...this.props.query.customMetrics, metric];
-    save(PROJECT_ACTIVITY_GRAPH_CUSTOM, customMetrics.join(','));
+    save(PROJECT_ACTIVITY_GRAPH_CUSTOM, customMetrics.join(','), this.props.project);
     this.props.updateQuery({ customMetrics });
   };
 
   removeCustomMetric = (removedMetric: string) => {
     const customMetrics = this.props.query.customMetrics.filter(metric => metric !== removedMetric);
-    save(PROJECT_ACTIVITY_GRAPH_CUSTOM, customMetrics.join(','));
+    save(PROJECT_ACTIVITY_GRAPH_CUSTOM, customMetrics.join(','), this.props.project);
     this.props.updateQuery({ customMetrics });
   };
 
   updateGraph = (graph: string) => {
-    save(PROJECT_ACTIVITY_GRAPH, graph);
+    save(PROJECT_ACTIVITY_GRAPH, graph, this.props.project);
     if (isCustomGraph(graph) && this.props.query.customMetrics.length <= 0) {
-      const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
-      this.props.updateQuery({ graph, customMetrics: customGraphs ? customGraphs.split(',') : [] });
+      const { customGraphs } = getProjectActivityGraph(this.props.project);
+      this.props.updateQuery({ graph, customMetrics: customGraphs });
     } else {
       this.props.updateQuery({ graph, customMetrics: [] });
     }
index 8b6b10378af2665e8f14f5d39b14d9d78012338c..0491de7cefa1590e6b4118a1e6fb09ec9706e855 100644 (file)
@@ -70,6 +70,7 @@ const DEFAULT_PROPS = {
   graphLoading: false,
   initializing: false,
   project: {
+    key: 'foo',
     leakPeriodDate: '2017-05-16T13:50:02+0200',
     qualifier: 'TRK'
   },
index edc467ca3eee0ac0867c3795b5faace477566eea..a6b578cb390358deeb886b182a1f2dfc64d2790a 100644 (file)
@@ -61,6 +61,7 @@ const DEFAULT_PROPS: ProjectActivityGraphs['props'] = {
     }
   ],
   metrics: METRICS,
+  project: 'foo',
   query: {
     category: '',
     customMetrics: [],
index ef110f70c997bb0d644758a21b316052e8e707c1..3e2758baaf490e94e5d0d3ea19300f05829867fa 100644 (file)
@@ -17,6 +17,7 @@ exports[`should render correctly 1`] = `
     category=""
     project={
       Object {
+        "key": "foo",
         "leakPeriodDate": "2017-05-16T13:50:02+0200",
         "qualifier": "TRK",
       }
@@ -78,6 +79,7 @@ exports[`should render correctly 1`] = `
         initializing={false}
         project={
           Object {
+            "key": "foo",
             "leakPeriodDate": "2017-05-16T13:50:02+0200",
             "qualifier": "TRK",
           }
@@ -162,6 +164,7 @@ exports[`should render correctly 1`] = `
             },
           ]
         }
+        project="foo"
         query={
           Object {
             "category": "",
index 201aaaf5d7d9a5432e95dcdd244057a4f136ad2a..fbb551988d6002c282636a984cba01f3fddb5179 100644 (file)
@@ -30,6 +30,7 @@ import {
 } from '../../helpers/query';
 import { parseDate, startOfDay } from '../../helpers/dates';
 import { getLocalizedMetricName, translate } from '../../helpers/l10n';
+import { get } from '../../helpers/storage';
 
 export type ParsedAnalysis = T.Omit<T.Analysis, 'date'> & { date: Date };
 
@@ -84,12 +85,8 @@ export const GRAPHS_METRICS: { [x: string]: string[] } = {
   duplications: GRAPHS_METRICS_DISPLAYED['duplications'].concat(['duplicated_lines_density'])
 };
 
-export const PROJECT_ACTIVITY_GRAPH = 'sonarqube.project_activity.graph';
-export const PROJECT_ACTIVITY_GRAPH_CUSTOM = 'sonarqube.project_activity.graph.custom';
-
-export function datesQueryChanged(prevQuery: Query, nextQuery: Query) {
-  return !isEqual(prevQuery.from, nextQuery.from) || !isEqual(prevQuery.to, nextQuery.to);
-}
+export const PROJECT_ACTIVITY_GRAPH = 'sonar_project_activity.graph';
+export const PROJECT_ACTIVITY_GRAPH_CUSTOM = 'sonar_project_activity.graph.custom';
 
 export function activityQueryChanged(prevQuery: Query, nextQuery: Query) {
   return prevQuery.category !== nextQuery.category || datesQueryChanged(prevQuery, nextQuery);
@@ -99,6 +96,10 @@ export function customMetricsChanged(prevQuery: Query, nextQuery: Query) {
   return !isEqual(prevQuery.customMetrics, nextQuery.customMetrics);
 }
 
+export function datesQueryChanged(prevQuery: Query, nextQuery: Query) {
+  return !isEqual(prevQuery.from, nextQuery.from) || !isEqual(prevQuery.to, nextQuery.to);
+}
+
 export function hasDataValues(serie: Serie) {
   return serie.data.some(point => Boolean(point.y || point.y === 0));
 }
@@ -246,6 +247,14 @@ export function getHistoryMetrics(graph: string, customMetrics: string[]) {
   return isCustomGraph(graph) ? customMetrics : GRAPHS_METRICS[graph];
 }
 
+export function getProjectActivityGraph(project: string) {
+  const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM, project);
+  return {
+    graph: get(PROJECT_ACTIVITY_GRAPH, project) || 'issues',
+    customGraphs: customGraphs ? customGraphs.split(',') : []
+  };
+}
+
 function parseGraph(value?: string) {
   const graph = parseAsString(value);
   return GRAPH_TYPES.includes(graph) ? graph : DEFAULT_GRAPH;
index a94d53e91ebfb1a7fdaa084856bed86d0cf24799..2709e650cb9e9151735d2cde8562f345bb045b35 100644 (file)
@@ -24,16 +24,14 @@ import PreviewGraphTooltips from './PreviewGraphTooltips';
 import AdvancedTimeline from '../charts/AdvancedTimeline';
 import {
   DEFAULT_GRAPH,
-  getDisplayedHistoryMetrics,
   generateSeries,
+  getDisplayedHistoryMetrics,
+  getProjectActivityGraph,
   getSeriesMetricType,
   hasHistoryDataValue,
-  PROJECT_ACTIVITY_GRAPH,
-  PROJECT_ACTIVITY_GRAPH_CUSTOM,
-  splitSeriesInGraphs,
-  Serie
+  Serie,
+  splitSeriesInGraphs
 } from '../../apps/projectActivity/utils';
-import { get } from '../../helpers/storage';
 import { formatMeasure, getShortType } from '../../helpers/measures';
 import { getBranchLikeQuery } from '../../helpers/branches';
 import { withRouter, Router } from '../hoc/withRouter';
@@ -67,9 +65,7 @@ const MAX_SERIES_PER_GRAPH = 3;
 class PreviewGraph extends React.PureComponent<Props, State> {
   constructor(props: Props) {
     super(props);
-    const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
-    const graph = get(PROJECT_ACTIVITY_GRAPH) || 'issues';
-    const customMetrics = customGraphs ? customGraphs.split(',') : [];
+    const { graph, customGraphs: customMetrics } = getProjectActivityGraph(props.project);
     const series = splitSeriesInGraphs(
       this.getSeries(props.history, graph, customMetrics, props.metrics),
       MAX_GRAPH_NB,
@@ -82,13 +78,11 @@ class PreviewGraph extends React.PureComponent<Props, State> {
     };
   }
 
-  componentWillReceiveProps(nextProps: Props) {
-    if (nextProps.history !== this.props.history || nextProps.metrics !== this.props.metrics) {
-      const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM);
-      const graph = get(PROJECT_ACTIVITY_GRAPH) || 'issues';
-      const customMetrics = customGraphs ? customGraphs.split(',') : [];
+  componentDidUpdate(prevProps: Props) {
+    if (prevProps.history !== this.props.history || prevProps.metrics !== this.props.metrics) {
+      const { graph, customGraphs: customMetrics } = getProjectActivityGraph(this.props.project);
       const series = splitSeriesInGraphs(
-        this.getSeries(nextProps.history, graph, customMetrics, nextProps.metrics),
+        this.getSeries(this.props.history, graph, customMetrics, this.props.metrics),
         MAX_GRAPH_NB,
         MAX_SERIES_PER_GRAPH
       );