From: Grégoire Aubert Date: Thu, 18 Jan 2018 08:10:50 +0000 (+0100) Subject: Fetch metrics with redux on project dashboard pages X-Git-Tag: 7.5~1776 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=57e81b11a8e94d3d21a24ff8b14aea368582b7f0;p=sonarqube.git Fetch metrics with redux on project dashboard pages --- diff --git a/server/sonar-web/src/main/js/app/components/App.tsx b/server/sonar-web/src/main/js/app/components/App.tsx index 2b83e2a6468..0b7375c9c93 100644 --- a/server/sonar-web/src/main/js/app/components/App.tsx +++ b/server/sonar-web/src/main/js/app/components/App.tsx @@ -38,6 +38,7 @@ interface State { canAdmin: boolean; loading: boolean; onSonarCloud: boolean; + organizationsEnabled: boolean; } class App extends React.PureComponent { @@ -46,19 +47,27 @@ class App extends React.PureComponent { static childContextTypes = { branchesEnabled: PropTypes.bool.isRequired, canAdmin: PropTypes.bool.isRequired, - onSonarCloud: PropTypes.bool + onSonarCloud: PropTypes.bool, + organizationsEnabled: PropTypes.bool }; constructor(props: Props) { super(props); - this.state = { branchesEnabled: false, canAdmin: false, loading: true, onSonarCloud: false }; + this.state = { + branchesEnabled: false, + canAdmin: false, + loading: true, + onSonarCloud: false, + organizationsEnabled: false + }; } getChildContext() { return { branchesEnabled: this.state.branchesEnabled, canAdmin: this.state.canAdmin, - onSonarCloud: this.state.onSonarCloud + onSonarCloud: this.state.onSonarCloud, + organizationsEnabled: this.state.organizationsEnabled }; } @@ -93,7 +102,8 @@ class App extends React.PureComponent { canAdmin: appState.canAdmin, onSonarCloud: Boolean( appState.settings && appState.settings['sonar.sonarcloud.enabled'] === 'true' - ) + ), + organizationsEnabled: appState.organizationsEnabled }); } return appState; diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index de3102a3957..75d48a09dff 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import * as PropTypes from 'prop-types'; import { connect } from 'react-redux'; import ComponentContainerNotFound from './ComponentContainerNotFound'; import ComponentNav from './nav/component/ComponentNav'; @@ -28,7 +29,6 @@ import { Task, getTasksForComponent } from '../../api/ce'; import { getComponentData } from '../../api/components'; import { getComponentNavigation } from '../../api/nav'; import { fetchOrganizations } from '../../store/rootActions'; -import { areThereCustomOrganizations } from '../../store/rootReducer'; import { STATUSES } from '../../apps/background-tasks/constants'; interface Props { @@ -37,7 +37,6 @@ interface Props { location: { query: { branch?: string; id: string }; }; - organizationsEnabled?: boolean; } interface State { @@ -52,6 +51,10 @@ interface State { export class ComponentContainer extends React.PureComponent { mounted: boolean; + static contextTypes = { + organizationsEnabled: PropTypes.bool + }; + constructor(props: Props) { super(props); this.state = { branches: [], loading: true }; @@ -98,7 +101,7 @@ export class ComponentContainer extends React.PureComponent { ([nav, data]) => { const component = this.addQualifier({ ...nav, ...data }); - if (this.props.organizationsEnabled) { + if (this.context.organizationsEnabled) { this.props.fetchOrganizations([component.organization]); } @@ -197,10 +200,6 @@ export class ComponentContainer extends React.PureComponent { } } -const mapStateToProps = (state: any) => ({ - organizationsEnabled: areThereCustomOrganizations(state) -}); - const mapDispatchToProps = { fetchOrganizations }; -export default connect(mapStateToProps, mapDispatchToProps)(ComponentContainer); +export default connect(null, mapDispatchToProps)(ComponentContainer); diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx index bb6c69b6098..b66a2aff8da 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx @@ -132,12 +132,10 @@ it('loads organization', async () => { const fetchOrganizations = jest.fn(); mount( - + - + , + { context: { organizationsEnabled: true } } ); await new Promise(setImmediate); @@ -150,12 +148,10 @@ it('fetches status', async () => { ); mount( - + - + , + { context: { organizationsEnabled: true } } ); await new Promise(setImmediate); diff --git a/server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx b/server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx index e0b1ca1d754..fc9a9db4554 100644 --- a/server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx +++ b/server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx @@ -18,43 +18,30 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { connect } from 'react-redux'; import Select from '../../../components/controls/Select'; import { fetchWebApi } from '../../../api/web-api'; -import { Metric } from '../../../app/types'; import { BadgeColors, BadgeType, BadgeOptions } from './utils'; import { getLocalizedMetricName, translate } from '../../../helpers/l10n'; -import { fetchMetrics } from '../../../store/rootActions'; -import { getMetrics } from '../../../store/rootReducer'; - -interface StateToProps { - metrics: { [key: string]: Metric }; -} - -interface DispatchToProps { - fetchMetrics: () => void; -} +import { Metric } from '../../../app/types'; -interface OwnProps { +interface Props { className?: string; + metrics: { [key: string]: Metric }; options: BadgeOptions; type: BadgeType; updateOptions: (options: Partial) => void; } -type Props = StateToProps & DispatchToProps & OwnProps; - interface State { badgeMetrics: string[]; } -export class BadgeParams extends React.PureComponent { +export default class BadgeParams extends React.PureComponent { mounted: boolean; state: State = { badgeMetrics: [] }; componentDidMount() { this.mounted = true; - this.props.fetchMetrics(); this.fetchBadgeMetrics(); } @@ -84,16 +71,14 @@ export class BadgeParams extends React.PureComponent { value: color })); - getMetricOptions = () => { - const { metrics } = this.props; - return this.state.badgeMetrics.map(key => { - const metric = metrics[key]; + getMetricOptions = () => + this.state.badgeMetrics.map(key => { + const metric = this.props.metrics[key]; return { value: key, - label: metric && getLocalizedMetricName(metric) + label: metric ? getLocalizedMetricName(metric) : key }; }); - }; handleColorChange = ({ value }: { value: BadgeColors }) => this.props.updateOptions({ color: value }); @@ -143,14 +128,3 @@ export class BadgeParams extends React.PureComponent { } } } - -const mapDispatchToProps: DispatchToProps = { fetchMetrics }; - -const mapStateToProps = (state: any): StateToProps => ({ - metrics: getMetrics(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BadgeParams); diff --git a/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx b/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx index 7e4e0daf249..ae664043b7c 100644 --- a/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx +++ b/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx @@ -22,12 +22,14 @@ import Modal from '../../../components/controls/Modal'; import BadgeButton from './BadgeButton'; import BadgeSnippet from './BadgeSnippet'; import BadgeParams from './BadgeParams'; -import { getBadgeUrl, BadgeType, BadgeOptions } from './utils'; +import { BadgeType, BadgeOptions, getBadgeUrl } from './utils'; +import { Metric } from '../../../app/types'; import { translate } from '../../../helpers/l10n'; import './styles.css'; interface Props { branch?: string; + metrics: { [key: string]: Metric }; project: string; } @@ -90,6 +92,7 @@ export default class BadgesModal extends React.PureComponent {

{ function getWrapper(props = {}) { return shallow( ({ })); it('should display the modal after click', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); click(wrapper.find('button')); expect(wrapper.find('Modal')).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap index 00e92664a30..f43e19a5715 100644 --- a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap @@ -61,8 +61,9 @@ exports[`should display the modal after click 2`] = ` > overview.badges.measure.description

- void; } +interface StateToProps { + metrics: { [key: string]: Metric }; +} + +interface DispatchToProps { + fetchMetrics: () => void; +} + +type Props = StateToProps & DispatchToProps & OwnProps; + interface State { history?: History; historyStartDate?: Date; @@ -53,12 +66,13 @@ interface State { periods?: Period[]; } -export default class OverviewApp extends React.PureComponent { +export class OverviewApp extends React.PureComponent { mounted: boolean; state: State = { loading: true, measures: [] }; componentDidMount() { this.mounted = true; + this.props.fetchMetrics(); this.loadMeasures().then(this.loadHistory, () => {}); } @@ -183,6 +197,7 @@ export default class OverviewApp extends React.PureComponent { component={component} history={history} measures={measures} + metrics={this.props.metrics} onComponentChange={this.props.onComponentChange} /> @@ -191,3 +206,14 @@ export default class OverviewApp extends React.PureComponent { ); } } + +const mapDispatchToProps: DispatchToProps = { fetchMetrics }; + +const mapStateToProps = (state: any): StateToProps => ({ + metrics: getMetrics(state) +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(OverviewApp); diff --git a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx index 30a3d98e7b1..ac3f80bb2a8 100644 --- a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx +++ b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx @@ -20,7 +20,6 @@ import * as React from 'react'; import { Link } from 'react-router'; import Analysis from './Analysis'; -import { getAllMetrics } from '../../../api/metrics'; import { getProjectActivity, Analysis as IAnalysis } from '../../../api/projectActivity'; import PreviewGraph from '../../../components/preview-graph/PreviewGraph'; import { translate } from '../../../helpers/l10n'; @@ -31,20 +30,20 @@ interface Props { branch?: string; component: Component; history?: History; + metrics: { [key: string]: Metric }; qualifier: string; } interface State { analyses: IAnalysis[]; loading: boolean; - metrics: Metric[]; } const PAGE_SIZE = 3; export default class AnalysesList extends React.PureComponent { mounted: boolean; - state: State = { analyses: [], loading: true, metrics: [] }; + state: State = { analyses: [], loading: true }; componentDidMount() { this.mounted = true; @@ -75,17 +74,15 @@ export default class AnalysesList extends React.PureComponent { fetchData = () => { this.setState({ loading: true }); - Promise.all([ - getProjectActivity({ - branch: this.props.branch, - project: this.getTopLevelComponent(), - ps: PAGE_SIZE - }), - getAllMetrics() - ]).then( - ([{ analyses }, metrics]) => { + + getProjectActivity({ + branch: this.props.branch, + project: this.getTopLevelComponent(), + ps: PAGE_SIZE + }).then( + ({ analyses }) => { if (this.mounted) { - this.setState({ analyses, metrics, loading: false }); + this.setState({ analyses, loading: false }); } }, () => { @@ -125,7 +122,7 @@ export default class AnalysesList extends React.PureComponent { branch={this.props.branch} history={this.props.history} project={this.props.component.key} - metrics={this.state.metrics} + metrics={this.props.metrics} /> {this.renderList(analyses)} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx b/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx index bfffecd1f7c..b84da6cb1f8 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { connect } from 'react-redux'; +import * as PropTypes from 'prop-types'; import MetaKey from './MetaKey'; import MetaOrganizationKey from './MetaOrganizationKey'; import MetaLinks from './MetaLinks'; @@ -28,90 +28,86 @@ import AnalysesList from '../events/AnalysesList'; import MetaSize from './MetaSize'; import MetaTags from './MetaTags'; import BadgesModal from '../badges/BadgesModal'; -import { areThereCustomOrganizations, getGlobalSettingValue } from '../../../store/rootReducer'; -import { Visibility, Component } from '../../../app/types'; +import { Visibility, Component, Metric } from '../../../app/types'; import { History } from '../../../api/time-machine'; import { MeasureEnhanced } from '../../../helpers/measures'; -interface OwnProps { +interface Props { branch?: string; component: Component; history?: History; measures: MeasureEnhanced[]; + metrics: { [key: string]: Metric }; onComponentChange: (changes: {}) => void; } -interface StateToProps { - areThereCustomOrganizations: boolean; - onSonarCloud: boolean; -} - -export function Meta(props: OwnProps & StateToProps) { - const { branch, component, areThereCustomOrganizations } = props; - const { qualifier, description, qualityProfiles, qualityGate, visibility } = component; - - const isProject = qualifier === 'TRK'; - const isPrivate = visibility === Visibility.Private; +export default class Meta extends React.PureComponent { + static contextTypes = { + onSonarCloud: PropTypes.bool, + organizationsEnabled: PropTypes.bool + }; - const hasDescription = !!description; - const hasQualityProfiles = Array.isArray(qualityProfiles) && qualityProfiles.length > 0; - const hasQualityGate = !!qualityGate; + render() { + const { onSonarCloud, organizationsEnabled } = this.context; + const { branch, component, metrics } = this.props; + const { qualifier, description, qualityProfiles, qualityGate, visibility } = component; - const shouldShowQualityProfiles = isProject && hasQualityProfiles; - const shouldShowQualityGate = isProject && hasQualityGate; - const hasOrganization = component.organization != null && areThereCustomOrganizations; + const isProject = qualifier === 'TRK'; + const isPrivate = visibility === Visibility.Private; - return ( -
- {hasDescription && ( -
{description}
- )} + const hasDescription = !!description; + const hasQualityProfiles = Array.isArray(qualityProfiles) && qualityProfiles.length > 0; + const hasQualityGate = !!qualityGate; - + const shouldShowQualityProfiles = isProject && hasQualityProfiles; + const shouldShowQualityGate = isProject && hasQualityGate; + const hasOrganization = component.organization != null && organizationsEnabled; - {isProject && } + return ( +
+ {hasDescription && ( +
{description}
+ )} - + - {shouldShowQualityGate && ( - - )} + {isProject && ( + + )} - {shouldShowQualityProfiles && ( - - )} - - {isProject && } - - - {hasOrganization && } - - {props.onSonarCloud && - isProject && - !isPrivate && } -
- ); + {shouldShowQualityGate && ( + + )} + + {shouldShowQualityProfiles && ( + + )} + + {isProject && } + + + + {hasOrganization && } + + {onSonarCloud && + isProject && + !isPrivate && } +
+ ); + } } - -const mapStateToProps = (state: any): StateToProps => { - const sonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled'); - return { - areThereCustomOrganizations: areThereCustomOrganizations(state), - onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true') - }; -}; - -export default connect(mapStateToProps)(Meta); diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx index e433633238e..b90026b8524 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx +++ b/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx @@ -20,8 +20,7 @@ import * as React from 'react'; import { getDisplayedHistoryMetrics, DEFAULT_GRAPH } from '../../projectActivity/utils'; import PreviewGraph from '../../../components/preview-graph/PreviewGraph'; -import { getAllMetrics } from '../../../api/metrics'; -import { getAllTimeMachineData } from '../../../api/time-machine'; +import { getAllTimeMachineData, History } from '../../../api/time-machine'; import { Metric } from '../../../app/types'; import { parseDate } from '../../../helpers/dates'; import { translate } from '../../../helpers/l10n'; @@ -29,18 +28,14 @@ import { getCustomGraph, getGraph } from '../../../helpers/storage'; const AnyPreviewGraph = PreviewGraph as any; -interface History { - [metric: string]: Array<{ date: Date; value: string }>; -} - interface Props { component: string; + metrics: { [key: string]: Metric }; } interface State { history?: History; loading: boolean; - metrics?: Metric[]; } export default class Activity extends React.PureComponent { @@ -71,8 +66,8 @@ export default class Activity extends React.PureComponent { } this.setState({ loading: true }); - return Promise.all([getAllTimeMachineData(component, graphMetrics), getAllMetrics()]).then( - ([timeMachine, metrics]) => { + return getAllTimeMachineData(component, graphMetrics).then( + timeMachine => { if (this.mounted) { const history: History = {}; timeMachine.measures.forEach(measure => { @@ -82,7 +77,7 @@ export default class Activity extends React.PureComponent { })); history[measure.metric] = measureHistory; }); - this.setState({ history, loading: false, metrics }); + this.setState({ history, loading: false }); } }, () => { @@ -103,11 +98,10 @@ export default class Activity extends React.PureComponent { {this.state.loading ? ( ) : ( - this.state.metrics !== undefined && this.state.history !== undefined && ( diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx index f5450166b5d..84ba6bbbf0a 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { connect } from 'react-redux'; import Summary from './Summary'; import Report from './Report'; import WorstProjects from './WorstProjects'; @@ -31,12 +32,25 @@ import { PORTFOLIO_METRICS, SUB_COMPONENTS_METRICS, convertMeasures } from '../u import { getMeasures } from '../../../api/measures'; import { getChildren } from '../../../api/components'; import { translate } from '../../../helpers/l10n'; +import { fetchMetrics } from '../../../store/rootActions'; +import { getMetrics } from '../../../store/rootReducer'; +import { Metric } from '../../../app/types'; import '../styles.css'; -interface Props { +interface OwnProps { component: { key: string; name: string }; } +interface StateToProps { + metrics: { [key: string]: Metric }; +} + +interface DispatchToProps { + fetchMetrics: () => void; +} + +type Props = StateToProps & DispatchToProps & OwnProps; + interface State { loading: boolean; measures?: { [key: string]: string | undefined }; @@ -44,12 +58,13 @@ interface State { totalSubComponents?: number; } -export default class App extends React.PureComponent { +export class App extends React.PureComponent { mounted: boolean; state: State = { loading: true }; componentDidMount() { this.mounted = true; + this.props.fetchMetrics(); this.fetchData(); } @@ -171,7 +186,7 @@ export default class App extends React.PureComponent { @@ -179,3 +194,13 @@ export default class App extends React.PureComponent { ); } } + +const mapDispatchToProps: DispatchToProps = { fetchMetrics }; + +const mapStateToProps = (state: any): StateToProps => ({ + metrics: getMetrics(state) +}); + +export default connect(mapStateToProps, mapDispatchToProps)( + App +); diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx index 8a9462dd697..c8c2e81f295 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx +++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx @@ -23,10 +23,6 @@ jest.mock('../../../../helpers/storage', () => ({ getGraph: () => 'custom' })); -jest.mock('../../../../api/metrics', () => ({ - getAllMetrics: jest.fn(() => Promise.resolve([])) -})); - jest.mock('../../../../api/time-machine', () => ({ getAllTimeMachineData: jest.fn(() => Promise.resolve({ @@ -47,17 +43,15 @@ import * as React from 'react'; import { mount, shallow } from 'enzyme'; import Activity from '../Activity'; -const getAllMetrics = require('../../../../api/metrics').getAllMetrics as jest.Mock; const getAllTimeMachineData = require('../../../../api/time-machine') .getAllTimeMachineData as jest.Mock; beforeEach(() => { - getAllMetrics.mockClear(); getAllTimeMachineData.mockClear(); }); it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.setState({ history: { coverage: [ @@ -72,7 +66,6 @@ it('renders', () => { }); it('fetches history', () => { - mount(); - expect(getAllMetrics).toBeCalled(); + mount(); expect(getAllTimeMachineData).toBeCalledWith('foo', ['coverage']); }); diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx index dc474149e9f..6cfe8d6890b 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx @@ -43,7 +43,7 @@ jest.mock('../Report', () => ({ import * as React from 'react'; import { shallow, mount } from 'enzyme'; -import App from '../App'; +import { App } from '../App'; const getMeasures = require('../../../../api/measures').getMeasures as jest.Mock; const getChildren = require('../../../../api/components').getChildren as jest.Mock; @@ -51,7 +51,7 @@ const getChildren = require('../../../../api/components').getChildren as jest.Mo const component = { key: 'foo', name: 'Foo' }; it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.setState({ loading: false, measures: { ncloc: '173', reliability_rating: '1' }, @@ -62,13 +62,13 @@ it('renders', () => { }); it('renders when portfolio is empty', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.setState({ loading: false, measures: { reliability_rating: '1' } }); expect(wrapper).toMatchSnapshot(); }); it('renders when portfolio is not computed', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.setState({ loading: false, measures: { ncloc: '173' } }); expect(wrapper).toMatchSnapshot(); }); @@ -76,7 +76,7 @@ it('renders when portfolio is not computed', () => { it('fetches measures and children components', () => { getMeasures.mockClear(); getChildren.mockClear(); - mount(); + mount(); expect(getMeasures).toBeCalledWith('foo', [ 'projects', 'ncloc', diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Activity-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Activity-test.tsx.snap index 2a0038daecb..d48d3089a5d 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Activity-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Activity-test.tsx.snap @@ -22,13 +22,7 @@ exports[`renders 1`] = ` ], } } - metrics={ - Array [ - Object { - "key": "coverage", - }, - ] - } + metrics={Object {}} project="foo" renderWhenEmpty={[Function]} /> diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/App-test.tsx.snap index 2e26c3cc408..76ccc5b4cb8 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/App-test.tsx.snap @@ -77,6 +77,7 @@ exports[`renders 1`] = ` /> | { [string]: Metric } */) { + if (Array.isArray(metrics)) { + return metrics.find(metric => metric.key === key); + } + return metrics[key]; +} + export function generateSeries( measuresHistory /*: Array */, graph /*: string */, - metrics /*: Array */, + metrics /*: Array | { [string]: Metric } */, displayedMetrics /*: Array */ ) /*: Array */ { if (displayedMetrics.length <= 0) { @@ -114,7 +121,7 @@ export function generateSeries( if (measure.metric === 'uncovered_lines' && !isCustomGraph(graph)) { return generateCoveredLinesMetric(measure, measuresHistory); } - const metric = metrics.find(metric => metric.key === measure.metric); + const metric = findMetric(measure.metric, metrics); return { data: measure.history.map(analysis => ({ x: analysis.date, diff --git a/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.d.ts b/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.d.ts index 744eed0493d..61de9d4f695 100644 --- a/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.d.ts +++ b/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.d.ts @@ -24,7 +24,7 @@ import { Metric } from '../../app/types'; interface Props { branch?: string; history?: History; - metrics: Metric[]; + metrics: { [key: string]: Metric }; project: string; renderWhenEmpty?: () => void; } diff --git a/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.js b/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.js index 9483414c1a8..8a8ac8443c5 100644 --- a/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.js +++ b/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.js @@ -41,7 +41,7 @@ import { formatMeasure, getShortType } from '../../helpers/measures'; type Props = { branch?: string, history: ?History, - metrics: Array, + metrics: { [string]: Metric }, project: string, renderWhenEmpty?: () => void }; @@ -121,7 +121,7 @@ export default class PreviewGraph extends React.PureComponent { history /*: ?History */, graph /*: string */, customMetrics /*: Array */, - metrics /*: Array */ + metrics /*: { [string]: Metric } */ ) => { const myHistory = history; if (!myHistory) {