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));
});
});
interface Props {
branchLike?: T.BranchLike;
component: T.ComponentMeasure;
- currentUser: T.CurrentUser;
location: { pathname: string; query: RawQuery };
fetchMeasures: (
component: string,
<MeasureOverviewContainer
branchLike={branchLike}
className="layout-page-main"
- currentUser={this.props.currentUser}
domain={query.metric}
leakPeriod={leakPeriod}
metrics={metrics}
<MeasureContentContainer
branchLike={branchLike}
className="layout-page-main"
- currentUser={this.props.currentUser}
fetchMeasures={fetchMeasures}
leakPeriod={leakPeriod}
metric={metric}
import { withRouter, WithRouterProps } from 'react-router';
import App from './App';
import throwGlobalError from '../../../app/utils/throwGlobalError';
-import { getCurrentUser, getMetrics, getMetricsKey } from '../../../store/rootReducer';
+import { getMetrics, getMetricsKey } from '../../../store/rootReducer';
import { fetchMetrics } from '../../../store/rootActions';
import { getMeasuresAndMeta } from '../../../api/measures';
import { getLeakPeriod } from '../../../helpers/periods';
import { getBranchLikeQuery } from '../../../helpers/branches';
interface StateToProps {
- currentUser: T.CurrentUser;
metrics: { [metric: string]: T.Metric };
metricsKey: string[];
}
}
const mapStateToProps = (state: any): StateToProps => ({
- currentUser: getCurrentUser(state),
metrics: getMetrics(state),
metricsKey: getMetricsKey(state)
});
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';
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;
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 {
metric?: T.Metric;
paging?: T.Paging;
selected?: string;
- view?: string;
}
export default class MeasureContent extends React.PureComponent<Props, State> {
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;
}
...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 });
}
renderMeasure() {
- const { metric, view } = this.state;
- if (metric !== undefined) {
- if (!view || ['list', 'tree'].includes(view)) {
- const selectedIdx = this.getSelectedIndex();
- return (
- <FilesView
- branchLike={this.props.branchLike}
- components={this.state.components}
- fetchMore={this.fetchMoreComponents}
- handleOpen={this.onOpenComponent}
- handleSelect={this.onSelectComponent}
- loadingMore={this.props.loadingMore}
- metric={metric}
- metrics={this.props.metrics}
- paging={this.state.paging}
- rootComponent={this.props.rootComponent}
- selectedIdx={selectedIdx}
- selectedKey={selectedIdx !== undefined ? this.state.selected : undefined}
- />
- );
- }
-
- if (view === 'treemap') {
- return (
- <TreeMapView
- branchLike={this.props.branchLike}
- components={this.state.components}
- handleSelect={this.onOpenComponent}
- metric={metric}
- />
- );
- }
+ const { view } = this.props;
+ const { metric } = this.state;
+ if (!metric) {
+ return null;
+ }
+ if (view === 'tree' || view === 'list') {
+ const selectedIdx = this.getSelectedIndex();
+ return (
+ <FilesView
+ branchLike={this.props.branchLike}
+ components={this.state.components}
+ defaultShowBestMeasures={view === 'tree'}
+ fetchMore={this.fetchMoreComponents}
+ handleOpen={this.onOpenComponent}
+ handleSelect={this.onSelectComponent}
+ loadingMore={this.props.loadingMore}
+ metric={metric}
+ metrics={this.props.metrics}
+ paging={this.state.paging}
+ rootComponent={this.props.rootComponent}
+ selectedIdx={selectedIdx}
+ selectedKey={selectedIdx !== undefined ? this.state.selected : undefined}
+ />
+ );
+ } else {
+ return (
+ <TreeMapView
+ branchLike={this.props.branchLike}
+ components={this.state.components}
+ handleSelect={this.onOpenComponent}
+ metric={metric}
+ />
+ );
}
-
- 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 (
<div className="layout-page-header-panel layout-page-main-header">
<div className="layout-page-header-panel-inner layout-page-main-header-inner">
<div className="layout-page-main-inner">
- <Breadcrumbs
- backToFirst={view === 'list'}
- branchLike={branchLike}
- className="measure-breadcrumbs spacer-right text-ellipsis"
- component={component}
- handleSelect={this.onOpenComponent}
- rootComponent={rootComponent}
- />
- {component.key !== rootComponent.key &&
- isLoggedIn(currentUser) && (
- <MeasureFavoriteContainer
+ <MeasureContentHeader
+ left={
+ <Breadcrumbs
+ backToFirst={view === 'list'}
branchLike={branchLike}
- className="measure-favorite spacer-right"
- component={component.key}
+ className="text-ellipsis flex-1"
+ component={component}
+ handleSelect={this.onOpenComponent}
+ rootComponent={rootComponent}
/>
- )}
- {!isFile && (
- <MeasureViewSelect
- className="measure-view-select"
- handleViewChange={this.props.updateView}
- metric={metric}
- view={view}
- />
- )}
- <PageActions
- current={
- selectedIdx !== undefined && view !== 'treemap' ? selectedIdx + 1 : undefined
}
- isFile={isFile}
- paging={this.state.paging}
- totalLoadedComponents={this.state.components.length}
- view={view}
+ right={
+ <div className="display-flex-center">
+ {!isFile && (
+ <MeasureViewSelect
+ className="measure-view-select big-spacer-right"
+ handleViewChange={this.props.updateView}
+ metric={metric}
+ view={view}
+ />
+ )}
+ <PageActions
+ current={
+ selectedIdx !== undefined && view !== 'treemap'
+ ? selectedIdx + 1
+ : undefined
+ }
+ isFile={isFile}
+ paging={this.state.paging}
+ totalLoadedComponents={this.state.components.length}
+ view={view}
+ />
+ </div>
+ }
/>
</div>
</div>
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,
router: InjectedRouter;
selected?: string;
updateQuery: (query: Partial<Query>) => void;
- view: string;
+ view: View;
}
interface LoadingState {
});
};
- updateView = (view: string) => this.props.updateQuery({ view });
+ updateView = (view: View) => {
+ this.props.updateQuery({ view });
+ };
render() {
if (!this.state.component) {
branchLike={this.props.branchLike}
className={this.props.className}
component={this.state.component}
- currentUser={this.props.currentUser}
leakPeriod={this.props.leakPeriod}
loading={this.state.loading.measure || this.state.loading.components}
loadingMore={this.state.loading.moreComponents}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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';
+
+interface Props {
+ left: React.ReactNode;
+ right: React.ReactNode;
+}
+
+export default function MeasureContentHeader({ left, right }: Props) {
+ return (
+ <div className="measure-content-header">
+ <div className="measure-content-header-left">{left}</div>
+ <div className="measure-content-header-right">{right}</div>
+ </div>
+ );
+}
+++ /dev/null
-/*
- * 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<T.SourceViewerFile, 'canMarkAsFavorite' | 'fav' | 'key' | 'q'>;
-
-interface Props {
- branchLike?: T.BranchLike;
- className?: string;
- component: string;
-}
-
-interface State {
- component?: FavComponent;
-}
-
-export default class MeasureFavoriteContainer extends React.PureComponent<Props, State> {
- 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 (
- <Favorite
- className={this.props.className}
- component={component.key}
- favorite={component.fav || false}
- qualifier={component.q}
- />
- );
- }
-}
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';
branchLike?: T.BranchLike;
className?: string;
component: T.ComponentMeasure;
- currentUser: T.CurrentUser;
domain: string;
leakPeriod?: T.Period;
loading: boolean;
}
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 (
<div className={this.props.className}>
<div className="layout-page-header-panel layout-page-main-header">
<div className="layout-page-header-panel-inner layout-page-main-header-inner">
<div className="layout-page-main-inner">
- <Breadcrumbs
- backToFirst={true}
- branchLike={branchLike}
- className="measure-breadcrumbs spacer-right text-ellipsis"
- component={component}
- handleSelect={this.props.updateSelected}
- rootComponent={rootComponent}
- />
- {component.key !== rootComponent.key &&
- isLoggedIn && (
- <MeasureFavoriteContainer
- className="measure-favorite spacer-right"
- component={component.key}
+ <MeasureContentHeader
+ left={
+ <Breadcrumbs
+ backToFirst={true}
+ branchLike={branchLike}
+ className="text-ellipsis"
+ component={component}
+ handleSelect={this.props.updateSelected}
+ rootComponent={rootComponent}
+ />
+ }
+ right={
+ <PageActions
+ current={this.state.components.length}
+ isFile={isFile}
+ paging={this.state.paging}
/>
- )}
- <PageActions
- current={this.state.components.length}
- isFile={isFile}
- paging={this.state.paging}
+ }
/>
</div>
</div>
interface Props {
branchLike?: T.BranchLike;
className?: string;
- currentUser: T.CurrentUser;
domain: string;
leakPeriod?: T.Period;
metrics: { [metric: string]: T.Metric };
branchLike={this.props.branchLike}
className={this.props.className}
component={this.state.component}
- currentUser={this.props.currentUser}
domain={this.props.domain}
leakPeriod={this.props.leakPeriod}
loading={this.state.loading.component || this.state.loading.bubbles}
import TreeIcon from '../../../components/icons-components/TreeIcon';
import TreemapIcon from '../../../components/icons-components/TreemapIcon';
import Select from '../../../components/controls/Select';
-import { hasList, hasTree, hasTreemap } from '../utils';
+import { hasList, hasTree, hasTreemap, View } from '../utils';
import { translate } from '../../../helpers/l10n';
interface Props {
className?: string;
metric: T.Metric;
- handleViewChange: (view: string) => void;
- view: string;
+ handleViewChange: (view: View) => void;
+ view: View;
}
export default class MeasureViewSelect extends React.PureComponent<Props> {
getOptions = () => {
const { metric } = this.props;
const options = [];
- if (hasList(metric.key)) {
- options.push({
- icon: <ListIcon />,
- label: translate('component_measures.tab.list'),
- value: 'list'
- });
- }
if (hasTree(metric.key)) {
options.push({
icon: <TreeIcon />,
value: 'tree'
});
}
+ if (hasList(metric.key)) {
+ options.push({
+ icon: <ListIcon />,
+ label: translate('component_measures.tab.list'),
+ value: 'list'
+ });
+ }
if (hasTreemap(metric.key, metric.type)) {
options.push({
icon: <TreemapIcon />,
};
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 }) => {
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 (
- <div className="pull-right">
+ <div className="display-flex-center">
{!isFile && showShortcuts && renderShortcuts()}
{isFile && paging && renderFileShortcuts()}
- <div className="measure-details-page-actions">
+ <div className="measure-details-page-actions nowrap">
{paging != null && (
<FilesCounter
className="spacer-left"
function renderShortcuts() {
return (
- <span className="note big-spacer-right">
+ <span className="note big-spacer-right nowrap">
<span className="big-spacer-right">
<span className="shortcut-button little-spacer-right">↑</span>
<span className="shortcut-button little-spacer-right">↓</span>
function renderFileShortcuts() {
return (
- <span className="note spacer-right">
+ <span className="note spacer-right nowrap">
<span>
<span className="shortcut-button little-spacer-right">j</span>
<span className="shortcut-button little-spacer-right">k</span>
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,
}
}
className="layout-page-main"
- currentUser={
- Object {
- "isLoggedIn": false,
- }
- }
fetchMeasures={
[MockFunction] {
"calls": Array [
}
selected=""
updateQuery={[Function]}
- view="list"
+ view="tree"
/>
</div>
</div>
optionRenderer={[Function]}
options={
Array [
- Object {
- "icon": <ListIcon />,
- "label": "component_measures.tab.list",
- "value": "list",
- },
Object {
"icon": <TreeIcon />,
"label": "component_measures.tab.tree",
"value": "tree",
},
+ Object {
+ "icon": <ListIcon />,
+ "label": "component_measures.tab.list",
+ "value": "list",
+ },
Object {
"icon": <TreemapIcon />,
"label": "component_measures.tab.treemap",
exports[`should display correctly for a file 1`] = `
<div
- className="pull-right"
+ className="display-flex-center"
>
<div
- className="measure-details-page-actions"
+ className="measure-details-page-actions nowrap"
/>
</div>
`;
exports[`should display correctly for a file 2`] = `
<div
- className="pull-right"
+ className="display-flex-center"
>
<span
- className="note spacer-right"
+ className="note spacer-right nowrap"
>
<span>
<span
</span>
</span>
<div
- className="measure-details-page-actions"
+ className="measure-details-page-actions nowrap"
>
<FilesCounter
className="spacer-left"
exports[`should display correctly for a project 1`] = `
<div
- className="pull-right"
+ className="display-flex-center"
>
<span
- className="note big-spacer-right"
+ className="note big-spacer-right nowrap"
>
<span
className="big-spacer-right"
</span>
</span>
<div
- className="measure-details-page-actions"
+ className="measure-details-page-actions nowrap"
/>
</div>
`;
exports[`should display the total of files 1`] = `
<div
- className="pull-right"
+ className="display-flex-center"
>
<div
- className="measure-details-page-actions"
+ className="measure-details-page-actions nowrap"
>
<FilesCounter
className="spacer-left"
exports[`should display the total of files 2`] = `
<div
- className="pull-right"
+ className="display-flex-center"
>
<span
- className="note spacer-right"
+ className="note spacer-right nowrap"
>
<span>
<span
</span>
</span>
<div
- className="measure-details-page-actions"
+ className="measure-details-page-actions nowrap"
>
<FilesCounter
className="spacer-left"
exports[`should not display shortcuts for treemap 1`] = `
<div
- className="pull-right"
+ className="display-flex-center"
>
<div
- className="measure-details-page-actions"
+ className="measure-details-page-actions nowrap"
/>
</div>
`;
interface Props {
branchLike?: T.BranchLike;
components: T.ComponentMeasureEnhanced[];
+ defaultShowBestMeasures: boolean;
fetchMore: () => void;
handleSelect: (component: string) => void;
handleOpen: (component: string) => void;
showBestMeasures: boolean;
}
-export default class ListView extends React.PureComponent<Props, State> {
+export default class FilesView extends React.PureComponent<Props, State> {
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);
}
this.scrollToElement();
}
if (prevProps.metric.key !== this.props.metric.key) {
- this.setState({ showBestMeasures: false });
+ this.setState({ showBestMeasures: this.props.defaultShowBestMeasures });
}
}
return shallow(
<FilesView
components={COMPONENTS}
+ defaultShowBestMeasures={false}
fetchMore={jest.fn()}
handleOpen={jest.fn()}
handleSelect={jest.fn()}
getLocalizedCategoryMetricName,
getLocalizedMetricDomain,
getLocalizedMetricName,
- translate
+ translate,
+ hasMessage
} from '../../../helpers/l10n';
interface Props {
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 (
<FacetBox property={domain.name}>
<FacetHeader
- helper={helper !== translatedHelper ? translatedHelper : undefined}
+ helper={helper}
name={getLocalizedMetricDomain(domain.name)}
onClick={this.handleHeaderClick}
open={this.props.open}
border-top-right-radius: 4px;
}
-.measure-breadcrumbs {
- display: inline-block;
- max-width: 60%;
- vertical-align: middle;
+.measure-content-header {
+ display: flex;
+ align-items: center;
+}
+
+.measure-content-header-left {
+ flex: 1;
+ min-width: 0;
+ white-space: nowrap;
+}
+
+.measure-content-header-right {
+ margin-left: calc(2 * var(--gridSize));
+ white-space: nowrap;
}
.measure-favorite svg {
import { isLongLivingBranch, isMainBranch } from '../../helpers/branches';
import { getDisplayMetrics } from '../../helpers/measures';
+export type View = 'list' | 'tree' | 'treemap';
+
export const PROJECT_OVERVEW = 'project_overview';
-export const DEFAULT_VIEW = 'list';
+export const DEFAULT_VIEW: View = 'tree';
export const DEFAULT_METRIC = PROJECT_OVERVEW;
export const KNOWN_DOMAINS = [
'Releasability',
]);
});
-export function getDefaultView(metric: string): string {
+export function getDefaultView(metric: string): View {
if (!hasList(metric)) {
return 'tree';
}
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)
});
});
(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
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() {