From f686185f25130fe4d84ae3b9307b784895414d5a Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Wed, 28 Nov 2018 13:52:56 +0100 Subject: [PATCH] SONAR-11478 Update the tree view on the Measures page (#982) --- .../__tests__/utils-test.ts | 9 +- .../component-measures/components/App.tsx | 3 - .../components/AppContainer.tsx | 4 +- .../components/MeasureContent.tsx | 184 +++++++++--------- .../components/MeasureContentContainer.tsx | 10 +- .../components/MeasureContentHeader.tsx | 34 ++++ .../components/MeasureFavoriteContainer.tsx | 85 -------- .../components/MeasureOverview.tsx | 41 ++-- .../components/MeasureOverviewContainer.tsx | 2 - .../components/MeasureViewSelect.tsx | 22 +-- .../components/PageActions.tsx | 11 +- .../components/__tests__/App-test.tsx | 1 - .../__tests__/__snapshots__/App-test.tsx.snap | 7 +- .../MeasureViewSelect-test.tsx.snap | 10 +- .../__snapshots__/PageActions-test.tsx.snap | 30 +-- .../drilldown/FilesView.tsx | 7 +- .../drilldown/__tests__/FilesView-test.tsx | 1 + .../sidebar/DomainFacet.tsx | 9 +- .../main/js/apps/component-measures/style.css | 18 +- .../main/js/apps/component-measures/utils.ts | 36 ++-- server/sonar-web/src/main/js/helpers/l10n.ts | 17 +- 21 files changed, 246 insertions(+), 295 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx delete mode 100644 server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.tsx diff --git a/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts index a1c7505e495..cecbc7dd5df 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts @@ -121,16 +121,17 @@ describe('parseQuery', () => { describe('serializeQuery', () => { it('should correctly serialize the query', () => { - expect(utils.serializeQuery({ metric: '', selected: '', view: 'list' })).toEqual({}); + expect(utils.serializeQuery({ metric: '', selected: '', view: 'list' })).toEqual({ + view: 'list' + }); expect(utils.serializeQuery({ metric: 'foo', selected: 'bar', view: 'tree' })).toEqual({ metric: 'foo', - selected: 'bar', - view: 'tree' + selected: 'bar' }); }); it('should be memoized', () => { - const query = { metric: 'foo', selected: 'bar', view: 'tree' }; + const query: utils.Query = { metric: 'foo', selected: 'bar', view: 'tree' }; expect(utils.serializeQuery(query)).toBe(utils.serializeQuery(query)); }); }); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx index 2d233e2e2a3..51de9019d2f 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx @@ -62,7 +62,6 @@ import '../style.css'; interface Props { branchLike?: T.BranchLike; component: T.ComponentMeasure; - currentUser: T.CurrentUser; location: { pathname: string; query: RawQuery }; fetchMeasures: ( component: string, @@ -200,7 +199,6 @@ export default class App extends React.PureComponent { { ({ - currentUser: getCurrentUser(state), metrics: getMetrics(state), metricsKey: getMetricsKey(state) }); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx index c254f67dd65..86982a68e0a 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import * as classNames from 'classnames'; import { InjectedRouter } from 'react-router'; import Breadcrumbs from './Breadcrumbs'; -import MeasureFavoriteContainer from './MeasureFavoriteContainer'; +import MeasureContentHeader from './MeasureContentHeader'; import MeasureHeader from './MeasureHeader'; import MeasureViewSelect from './MeasureViewSelect'; import MetricNotFound from './MetricNotFound'; @@ -31,19 +31,17 @@ import CodeView from '../drilldown/CodeView'; import TreeMapView from '../drilldown/TreeMapView'; import { getComponentTree } from '../../../api/components'; import { complementary } from '../config/complementary'; -import { enhanceComponent, isFileType, isViewType } from '../utils'; +import { enhanceComponent, isFileType, isViewType, View } from '../utils'; import { getProjectUrl } from '../../../helpers/urls'; import { isDiffMetric } from '../../../helpers/measures'; import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; import { RequestData } from '../../../helpers/request'; -import { isLoggedIn } from '../../../helpers/users'; interface Props { branchLike?: T.BranchLike; className?: string; component: T.ComponentMeasure; - currentUser: T.CurrentUser; loading: boolean; loadingMore: boolean; leakPeriod?: T.Period; @@ -55,8 +53,8 @@ interface Props { secondaryMeasure?: T.MeasureEnhanced; updateLoading: (param: { [key: string]: boolean }) => void; updateSelected: (component: string) => void; - updateView: (view: string) => void; - view: string; + updateView: (view: View) => void; + view: View; } interface State { @@ -64,7 +62,6 @@ interface State { metric?: T.Metric; paging?: T.Paging; selected?: string; - view?: string; } export default class MeasureContent extends React.PureComponent { @@ -99,37 +96,47 @@ export default class MeasureContent extends React.PureComponent { return index !== -1 ? index : undefined; }; - getComponentRequestParams = (view: string, metric: T.Metric, options: Object = {}) => { + getComponentRequestParams = (view: View, metric: T.Metric, options: Object = {}) => { const strategy = view === 'list' ? 'leaves' : 'children'; const metricKeys = [metric.key]; const opts: RequestData = { ...getBranchLikeQuery(this.props.branchLike), additionalFields: 'metrics', - metricSortFilter: 'withMeasuresOnly' + ps: 500 }; - const isDiff = isDiffMetric(metric.key); - if (isDiff) { - opts.metricPeriodSort = 1; - } - if (view === 'treemap') { - const sizeMetric = isDiff ? 'new_lines' : 'ncloc'; - metricKeys.push(sizeMetric); - opts.metricSort = sizeMetric; + + const setMetricSort = () => { + const isDiff = isDiffMetric(metric.key); opts.s = isDiff ? 'metricPeriod' : 'metric'; - opts.asc = false; - } else { + opts.metricSortFilter = 'withMeasuresOnly'; + if (isDiff) { + opts.metricPeriodSort = 1; + } + }; + + const isDiff = isDiffMetric(metric.key); + if (view === 'tree') { + metricKeys.push(...(complementary[metric.key] || [])); + opts.asc = true; + opts.s = 'qualifier,name'; + } else if (view === 'list') { metricKeys.push(...(complementary[metric.key] || [])); opts.asc = metric.direction === 1; - opts.ps = 100; opts.metricSort = metric.key; - opts.s = isDiff ? 'metricPeriod' : 'metric'; + setMetricSort(); + } else if (view === 'treemap') { + const sizeMetric = isDiff ? 'new_lines' : 'ncloc'; + metricKeys.push(sizeMetric); + opts.asc = false; + opts.metricSort = sizeMetric; + setMetricSort(); } + return { metricKeys, opts: { ...opts, ...options }, strategy }; }; fetchComponents = ({ component, metric, metrics, view }: Props) => { if (isFileType(component)) { - this.setState({ metric: undefined, view: undefined }); return; } @@ -178,9 +185,9 @@ export default class MeasureContent extends React.PureComponent { ...state.components, ...r.components.map(component => enhanceComponent(component, metric, metrics)) ], + // merge to get the metric best value metric: { ...metric, ...r.metrics.find(m => m.key === metric.key) }, - paging: r.paging, - view + paging: r.paging })); } this.props.updateLoading({ moreComponents: false }); @@ -228,45 +235,44 @@ export default class MeasureContent extends React.PureComponent { } renderMeasure() { - const { metric, view } = this.state; - if (metric !== undefined) { - if (!view || ['list', 'tree'].includes(view)) { - const selectedIdx = this.getSelectedIndex(); - return ( - - ); - } - - if (view === 'treemap') { - return ( - - ); - } + const { view } = this.props; + const { metric } = this.state; + if (!metric) { + return null; + } + if (view === 'tree' || view === 'list') { + const selectedIdx = this.getSelectedIndex(); + return ( + + ); + } else { + return ( + + ); } - - return null; } render() { - const { branchLike, component, currentUser, measure, metric, rootComponent, view } = this.props; + const { branchLike, component, measure, metric, rootComponent, view } = this.props; const isFile = isFileType(component); const selectedIdx = this.getSelectedIndex(); return ( @@ -276,38 +282,40 @@ export default class MeasureContent extends React.PureComponent {
- - {component.key !== rootComponent.key && - isLoggedIn(currentUser) && ( - - )} - {!isFile && ( - - )} - + {!isFile && ( + + )} + +
+ } />
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx index 2695dc15b32..cd6edf18d8b 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.tsx @@ -20,12 +20,11 @@ import * as React from 'react'; import { InjectedRouter } from 'react-router'; import MeasureContent from './MeasureContent'; -import { Query } from '../utils'; +import { Query, View } from '../utils'; interface Props { branchLike?: T.BranchLike; className?: string; - currentUser: T.CurrentUser; rootComponent: T.ComponentMeasure; fetchMeasures: ( component: string, @@ -38,7 +37,7 @@ interface Props { router: InjectedRouter; selected?: string; updateQuery: (query: Partial) => void; - view: string; + view: View; } interface LoadingState { @@ -111,7 +110,9 @@ export default class MeasureContentContainer extends React.PureComponent this.props.updateQuery({ view }); + updateView = (view: View) => { + this.props.updateQuery({ view }); + }; render() { if (!this.state.component) { @@ -123,7 +124,6 @@ export default class MeasureContentContainer extends React.PureComponent +
{left}
+
{right}
+ + ); +} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.tsx deleted file mode 100644 index 3ad335a4d31..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 Favorite from '../../../components/controls/Favorite'; -import { getComponentForSourceViewer } from '../../../api/components'; -import { isMainBranch } from '../../../helpers/branches'; - -type FavComponent = Pick; - -interface Props { - branchLike?: T.BranchLike; - className?: string; - component: string; -} - -interface State { - component?: FavComponent; -} - -export default class MeasureFavoriteContainer extends React.PureComponent { - mounted = false; - state: State = {}; - - componentDidMount() { - this.mounted = true; - this.fetchComponentFavorite(); - } - - componentDidUpdate(prevProps: Props) { - if (prevProps.component !== this.props.component) { - this.fetchComponentFavorite(); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchComponentFavorite() { - getComponentForSourceViewer({ component: this.props.component }).then( - component => { - if (this.mounted) { - this.setState({ component }); - } - }, - () => {} - ); - } - - render() { - const { component } = this.state; - if ( - !component || - !component.canMarkAsFavorite || - (this.props.branchLike && !isMainBranch(this.props.branchLike)) - ) { - return null; - } - return ( - - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx index f41816f6a21..e18c6821108 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import Breadcrumbs from './Breadcrumbs'; import LeakPeriodLegend from './LeakPeriodLegend'; -import MeasureFavoriteContainer from './MeasureFavoriteContainer'; +import MeasureContentHeader from './MeasureContentHeader'; import PageActions from './PageActions'; import BubbleChart from '../drilldown/BubbleChart'; import SourceViewer from '../../../components/SourceViewer/SourceViewer'; @@ -33,7 +33,6 @@ interface Props { branchLike?: T.BranchLike; className?: string; component: T.ComponentMeasure; - currentUser: T.CurrentUser; domain: string; leakPeriod?: T.Period; loading: boolean; @@ -133,33 +132,31 @@ export default class MeasureOverview extends React.PureComponent { } render() { - const { branchLike, component, currentUser, leakPeriod, rootComponent } = this.props; - const isLoggedIn = currentUser && currentUser.isLoggedIn; + const { branchLike, component, leakPeriod, rootComponent } = this.props; const isFile = isFileType(component); return (
- - {component.key !== rootComponent.key && - isLoggedIn && ( - + } + right={ + - )} -
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx index 3f2dbfbba2e..200fce078a2 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx @@ -28,7 +28,6 @@ import { getBranchLikeQuery } from '../../../helpers/branches'; interface Props { branchLike?: T.BranchLike; className?: string; - currentUser: T.CurrentUser; domain: string; leakPeriod?: T.Period; metrics: { [metric: string]: T.Metric }; @@ -119,7 +118,6 @@ export default class MeasureOverviewContainer extends React.PureComponent void; - view: string; + handleViewChange: (view: View) => void; + view: View; } export default class MeasureViewSelect extends React.PureComponent { getOptions = () => { const { metric } = this.props; const options = []; - if (hasList(metric.key)) { - options.push({ - icon: , - label: translate('component_measures.tab.list'), - value: 'list' - }); - } if (hasTree(metric.key)) { options.push({ icon: , @@ -50,6 +43,13 @@ export default class MeasureViewSelect extends React.PureComponent { value: 'tree' }); } + if (hasList(metric.key)) { + options.push({ + icon: , + label: translate('component_measures.tab.list'), + value: 'list' + }); + } if (hasTreemap(metric.key, metric.type)) { options.push({ icon: , @@ -61,7 +61,7 @@ export default class MeasureViewSelect extends React.PureComponent { }; handleChange = (option: { value: string }) => { - return this.props.handleViewChange(option.value); + return this.props.handleViewChange(option.value as View); }; renderOption = (option: { icon: JSX.Element; label: string }) => { diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.tsx index e2c57b91269..32dc2706fe7 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.tsx @@ -20,23 +20,24 @@ import * as React from 'react'; import FilesCounter from './FilesCounter'; import { translate } from '../../../helpers/l10n'; +import { View } from '../utils'; interface Props { current?: number; isFile?: boolean; paging?: T.Paging; totalLoadedComponents?: number; - view?: string; + view?: View; } export default function PageActions(props: Props) { const { isFile, paging, totalLoadedComponents } = props; const showShortcuts = props.view && ['list', 'tree'].includes(props.view); return ( -
+
{!isFile && showShortcuts && renderShortcuts()} {isFile && paging && renderFileShortcuts()} -
+
{paging != null && ( + ↑ ↓ @@ -69,7 +70,7 @@ function renderShortcuts() { function renderFileShortcuts() { return ( - + j k diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx index 939481b7480..de3394ecc88 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx @@ -46,7 +46,6 @@ const METRICS = { const PROPS: App['props'] = { branchLike: { isMain: true, name: 'master' }, component: COMPONENT, - currentUser: { isLoggedIn: false }, location: { pathname: '/component_measures', query: { metric: 'coverage' } }, fetchMeasures: jest.fn().mockResolvedValue({ component: COMPONENT, diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap index df50df20427..56e8fe4c14f 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap @@ -81,11 +81,6 @@ exports[`should render correctly 1`] = ` } } className="layout-page-main" - currentUser={ - Object { - "isLoggedIn": false, - } - } fetchMeasures={ [MockFunction] { "calls": Array [ @@ -166,7 +161,7 @@ exports[`should render correctly 1`] = ` } selected="" updateQuery={[Function]} - view="list" + view="tree" />
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap index 43cd04e0644..bc440003070 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap @@ -8,16 +8,16 @@ exports[`should display correctly with treemap option 1`] = ` optionRenderer={[Function]} options={ Array [ - Object { - "icon": , - "label": "component_measures.tab.list", - "value": "list", - }, Object { "icon": , "label": "component_measures.tab.tree", "value": "tree", }, + Object { + "icon": , + "label": "component_measures.tab.list", + "value": "list", + }, Object { "icon": , "label": "component_measures.tab.treemap", diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.tsx.snap index 9849310b4c2..002f43d4c7e 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.tsx.snap @@ -2,20 +2,20 @@ exports[`should display correctly for a file 1`] = `
`; exports[`should display correctly for a file 2`] = `
`; exports[`should display the total of files 1`] = `
`; diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx index e9621984a29..d4f364f6f34 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx @@ -31,6 +31,7 @@ import { Alert } from '../../../components/ui/Alert'; interface Props { branchLike?: T.BranchLike; components: T.ComponentMeasureEnhanced[]; + defaultShowBestMeasures: boolean; fetchMore: () => void; handleSelect: (component: string) => void; handleOpen: (component: string) => void; @@ -47,12 +48,12 @@ interface State { showBestMeasures: boolean; } -export default class ListView extends React.PureComponent { +export default class FilesView extends React.PureComponent { listContainer?: HTMLElement | null; constructor(props: Props) { super(props); - this.state = { showBestMeasures: false }; + this.state = { showBestMeasures: props.defaultShowBestMeasures }; this.selectNext = throttle(this.selectNext, 100); this.selectPrevious = throttle(this.selectPrevious, 100); } @@ -69,7 +70,7 @@ export default class ListView extends React.PureComponent { this.scrollToElement(); } if (prevProps.metric.key !== this.props.metric.key) { - this.setState({ showBestMeasures: false }); + this.setState({ showBestMeasures: this.props.defaultShowBestMeasures }); } } diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx index 8001e681cbd..425024d3e86 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx @@ -58,6 +58,7 @@ function getWrapper(props = {}) { return shallow( { render() { const { domain } = this.props; - const helper = `component_measures.domain_facets.${domain.name}.help`; - const translatedHelper = translate(helper); + const helperMessageKey = `component_measures.domain_facets.${domain.name}.help`; + const helper = hasMessage(helperMessageKey) ? translate(helperMessageKey) : undefined; return ( { ]); }); -export function getDefaultView(metric: string): string { +export function getDefaultView(metric: string): View { if (!hasList(metric)) { return 'tree'; } @@ -196,35 +198,37 @@ export function isProjectOverview(metric: string) { return metric === PROJECT_OVERVEW; } -const parseView = (metric: string, rawView?: string) => { - const view = parseAsString(rawView) || DEFAULT_VIEW; +function parseView(metric: string, rawView?: string): View { + const view = (parseAsString(rawView) || DEFAULT_VIEW) as View; if (!hasTree(metric)) { return 'list'; } else if (view === 'list' && !hasList(metric)) { return 'tree'; } return view; -}; +} export interface Query { metric: string; selected?: string; - view: string; + view: View; } -export const parseQuery = memoize((urlQuery: RawQuery) => { - const metric = parseAsString(urlQuery['metric']) || DEFAULT_METRIC; - return { - metric, - selected: parseAsString(urlQuery['selected']), - view: parseView(metric, urlQuery['view']) - }; -}); +export const parseQuery = memoize( + (urlQuery: RawQuery): Query => { + const metric = parseAsString(urlQuery['metric']) || DEFAULT_METRIC; + return { + metric, + selected: parseAsString(urlQuery['selected']), + view: parseView(metric, urlQuery['view']) + }; + } +); export const serializeQuery = memoize((query: Query) => { return cleanQuery({ - metric: query.metric === DEFAULT_METRIC ? null : serializeString(query.metric), + metric: query.metric === DEFAULT_METRIC ? undefined : serializeString(query.metric), selected: serializeString(query.selected), - view: query.view === DEFAULT_VIEW ? null : serializeString(query.view) + view: query.view === DEFAULT_VIEW ? undefined : serializeString(query.view) }); }); diff --git a/server/sonar-web/src/main/js/helpers/l10n.ts b/server/sonar-web/src/main/js/helpers/l10n.ts index 4da10579dd1..6e13e95de91 100644 --- a/server/sonar-web/src/main/js/helpers/l10n.ts +++ b/server/sonar-web/src/main/js/helpers/l10n.ts @@ -147,12 +147,6 @@ export function installGlobal() { (window as any).requestMessages = requestMessages; } -export function getLocalizedDashboardName(baseName: string) { - const l10nKey = `dashboard.${baseName}.name`; - const l10nLabel = translate(l10nKey); - return l10nLabel !== l10nKey ? l10nLabel : baseName; -} - export function getLocalizedMetricName( metric: { key: string; name?: string }, short?: boolean @@ -160,24 +154,21 @@ export function getLocalizedMetricName( const bundleKey = `metric.${metric.key}.${short ? 'short_name' : 'name'}`; if (hasMessage(bundleKey)) { return translate(bundleKey); + } else if (short) { + return getLocalizedMetricName(metric); } else { - if (short) { - return getLocalizedMetricName(metric); - } return metric.name || metric.key; } } export function getLocalizedCategoryMetricName(metric: { key: string; name?: string }) { const bundleKey = `metric.${metric.key}.extra_short_name`; - const fromBundle = translate(bundleKey); - return fromBundle === bundleKey ? getLocalizedMetricName(metric, true) : fromBundle; + return hasMessage(bundleKey) ? translate(bundleKey) : getLocalizedMetricName(metric, true); } export function getLocalizedMetricDomain(domainName: string) { const bundleKey = `metric_domain.${domainName}`; - const fromBundle = translate(bundleKey); - return fromBundle !== bundleKey ? fromBundle : domainName; + return hasMessage(bundleKey) ? translate(bundleKey) : domainName; } export function getCurrentLocale() { -- 2.39.5