aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/component-measures
diff options
context:
space:
mode:
authorWouter Admiraal <45544358+wouter-admiraal-sonarsource@users.noreply.github.com>2021-08-30 12:10:31 +0200
committersonartech <sonartech@sonarsource.com>2021-08-30 20:08:19 +0000
commit49789f6a622f402e85a72b9b77b9dfa827785a60 (patch)
tree065bd83c142248fdb576510da973147fbe3bf6e7 /server/sonar-web/src/main/js/apps/component-measures
parent1e78c066e5eed1752abfbcc91b977703d5dd0c51 (diff)
downloadsonarqube-49789f6a622f402e85a72b9b77b9dfa827785a60.tar.gz
sonarqube-49789f6a622f402e85a72b9b77b9dfa827785a60.zip
SONAR-12018 Keep users on Measures page when drilling down
Diffstat (limited to 'server/sonar-web/src/main/js/apps/component-measures')
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap51
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx168
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx106
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap334
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/utils.ts11
18 files changed, 595 insertions, 170 deletions
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 a117429d6fa..c5c6b915305 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
@@ -31,11 +31,12 @@ import { RequestData } from '../../../helpers/request';
import { scrollToElement } from '../../../helpers/scrolling';
import { getProjectUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
+import { MeasurePageView } from '../../../types/measures';
import { MetricKey } from '../../../types/metrics';
import { complementary } from '../config/complementary';
import FilesView from '../drilldown/FilesView';
import TreeMapView from '../drilldown/TreeMapView';
-import { enhanceComponent, isFileType, isViewType, Query, View } from '../utils';
+import { enhanceComponent, isFileType, isViewType, Query } from '../utils';
import Breadcrumbs from './Breadcrumbs';
import MeasureContentHeader from './MeasureContentHeader';
import MeasureHeader from './MeasureHeader';
@@ -51,7 +52,7 @@ interface Props {
router: InjectedRouter;
selected?: string;
updateQuery: (query: Partial<Query>) => void;
- view: View;
+ view: MeasurePageView;
}
interface State {
@@ -170,7 +171,7 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
};
getComponentRequestParams(
- view: View,
+ view: MeasurePageView,
metric: Pick<T.Metric, 'key' | 'direction'>,
options: Object = {}
) {
@@ -218,7 +219,7 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
});
};
- updateView = (view: View) => {
+ updateView = (view: MeasurePageView) => {
this.props.updateQuery({ view });
};
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx
index c6860eb3066..43da92b6dcb 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx
@@ -23,13 +23,14 @@ import ListIcon from '../../../components/icons/ListIcon';
import TreeIcon from '../../../components/icons/TreeIcon';
import TreemapIcon from '../../../components/icons/TreemapIcon';
import { translate } from '../../../helpers/l10n';
-import { hasList, hasTree, hasTreemap, View } from '../utils';
+import { MeasurePageView } from '../../../types/measures';
+import { hasList, hasTree, hasTreemap } from '../utils';
interface Props {
className?: string;
metric: T.Metric;
- handleViewChange: (view: View) => void;
- view: View;
+ handleViewChange: (view: MeasurePageView) => void;
+ view: MeasurePageView;
}
export default class MeasureViewSelect extends React.PureComponent<Props> {
@@ -61,7 +62,7 @@ export default class MeasureViewSelect extends React.PureComponent<Props> {
};
handleChange = (option: { value: string }) => {
- return this.props.handleViewChange(option.value as View);
+ return this.props.handleViewChange(option.value as MeasurePageView);
};
renderOption = (option: { icon: JSX.Element; label: string }) => {
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 9c9ae1d407e..c805df0da81 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
@@ -21,7 +21,8 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import { getMeasuresWithPeriod } from '../../../../api/measures';
import { mockMainBranch, mockPullRequest } from '../../../../helpers/mocks/branch-like';
-import { mockComponent, mockIssue, mockLocation, mockRouter } from '../../../../helpers/testMocks';
+import { mockComponent } from '../../../../helpers/mocks/component';
+import { mockIssue, mockLocation, mockRouter } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { App } from '../App';
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx
index d89eb1f0c04..1071d5f1fba 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx
@@ -20,8 +20,9 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { getComponentTree } from '../../../../api/components';
+import { mockComponentMeasure } from '../../../../helpers/mocks/component';
import { scrollToElement } from '../../../../helpers/scrolling';
-import { mockComponentMeasure, mockRouter } from '../../../../helpers/testMocks';
+import { mockRouter } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import MeasureContent from '../MeasureContent';
@@ -30,7 +31,7 @@ jest.mock('../../../../helpers/scrolling', () => ({
}));
jest.mock('../../../../api/components', () => {
- const { mockComponentMeasure } = jest.requireActual('../../../../helpers/testMocks');
+ const { mockComponentMeasure } = jest.requireActual('../../../../helpers/mocks/component');
return {
getComponentTree: jest.fn().mockResolvedValue({
paging: { pageIndex: 1, pageSize: 500, total: 2 },
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx
index 77923e3f982..4d368739eb1 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx
@@ -23,9 +23,11 @@ import { keyBy } from 'lodash';
import * as React from 'react';
import { getComponentLeaves } from '../../../../api/components';
import { mockBranch } from '../../../../helpers/mocks/branch-like';
-import { mockComponentMeasureEnhanced } from '../../../../helpers/mocks/component';
import {
mockComponentMeasure,
+ mockComponentMeasureEnhanced
+} from '../../../../helpers/mocks/component';
+import {
mockMeasure,
mockMeasureEnhanced,
mockMetric,
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx
index 161f47f2a34..302bf0bf71c 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx
@@ -17,18 +17,34 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockMetric } from '../../../../helpers/testMocks';
+import { MetricKey } from '../../../../types/metrics';
import MeasureViewSelect from '../MeasureViewSelect';
-it('should display correctly with treemap option', () => {
+it('should render correctly', () => {
+ expect(
+ shallowRender({ metric: mockMetric({ key: MetricKey.releasability_rating }) })
+ ).toMatchSnapshot('has no list');
expect(
- shallow(
- <MeasureViewSelect
- handleViewChange={() => {}}
- metric={{ type: 'PERCENT' } as T.Metric}
- view="tree"
- />
- )
- ).toMatchSnapshot();
+ shallowRender({ metric: mockMetric({ key: MetricKey.alert_status, type: 'LEVEL' }) })
+ ).toMatchSnapshot('has no tree');
+ expect(shallowRender({ metric: mockMetric({ type: 'RATING' }) })).toMatchSnapshot(
+ 'has no treemap'
+ );
});
+
+it('should correctly trigger a selection change', () => {
+ const handleViewChange = jest.fn();
+ const wrapper = shallowRender({ handleViewChange });
+ wrapper.instance().handleChange({ value: 'list' });
+ expect(handleViewChange).toBeCalledWith('list');
+});
+
+function shallowRender(props: Partial<MeasureViewSelect['props']> = {}) {
+ return shallow<MeasureViewSelect>(
+ <MeasureViewSelect metric={mockMetric()} handleViewChange={jest.fn()} view="list" {...props} />
+ );
+}
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 bc440003070..29fe3dbd9eb 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
@@ -1,6 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should display correctly with treemap option 1`] = `
+exports[`should render correctly: has no list 1`] = `
+<Select
+ autoBlur={true}
+ clearable={false}
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "icon": <TreeIcon />,
+ "label": "component_measures.tab.tree",
+ "value": "tree",
+ },
+ Object {
+ "icon": <TreemapIcon />,
+ "label": "component_measures.tab.treemap",
+ "value": "treemap",
+ },
+ ]
+ }
+ searchable={false}
+ value="list"
+ valueRenderer={[Function]}
+/>
+`;
+
+exports[`should render correctly: has no tree 1`] = `
+<Select
+ autoBlur={true}
+ clearable={false}
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "icon": <ListIcon />,
+ "label": "component_measures.tab.list",
+ "value": "list",
+ },
+ ]
+ }
+ searchable={false}
+ value="list"
+ valueRenderer={[Function]}
+/>
+`;
+
+exports[`should render correctly: has no treemap 1`] = `
<Select
autoBlur={true}
clearable={false}
@@ -26,7 +73,7 @@ exports[`should display correctly with treemap option 1`] = `
]
}
searchable={false}
- value="tree"
+ value="list"
valueRenderer={[Function]}
/>
`;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx
index 7a5e0cf70d5..9b81111a7db 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx
@@ -17,123 +17,113 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { LocationDescriptor } from 'history';
import * as React from 'react';
import { Link } from 'react-router';
import BranchIcon from '../../../components/icons/BranchIcon';
import LinkIcon from '../../../components/icons/LinkIcon';
import QualifierIcon from '../../../components/icons/QualifierIcon';
import { translate } from '../../../helpers/l10n';
-import { isDiffMetric } from '../../../helpers/measures';
import { splitPath } from '../../../helpers/path';
import {
getBranchLikeUrl,
getComponentDrilldownUrlWithSelection,
- getComponentSecurityHotspotsUrl,
getProjectUrl
} from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
-import { isFileType, isSecurityReviewMetric, View } from '../utils';
+import {
+ ComponentQualifier,
+ isApplication,
+ isPortfolioLike,
+ isProject
+} from '../../../types/component';
+import { MeasurePageView } from '../../../types/measures';
+import { MetricKey } from '../../../types/metrics';
-interface Props {
+export interface ComponentCellProps {
branchLike?: BranchLike;
component: T.ComponentMeasureEnhanced;
- onClick: (component: string) => void;
metric: T.Metric;
rootComponent: T.ComponentMeasure;
- view: View;
+ view: MeasurePageView;
}
-export default class ComponentCell extends React.PureComponent<Props> {
- handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
- const isLeftClickEvent = event.button === 0;
- const isModifiedEvent = !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
+export default function ComponentCell(props: ComponentCellProps) {
+ const { branchLike, component, metric, rootComponent, view } = props;
- if (isLeftClickEvent && !isModifiedEvent) {
- event.preventDefault();
- this.props.onClick(this.props.component.key);
- }
- };
+ let head = '';
+ let tail = component.name;
- renderInner(componentKey: string) {
- const { component } = this.props;
- let head = '';
- let tail = component.name;
+ if (
+ view === 'list' &&
+ ([
+ ComponentQualifier.File,
+ ComponentQualifier.TestFile,
+ ComponentQualifier.Directory
+ ] as string[]).includes(component.qualifier) &&
+ component.path
+ ) {
+ ({ head, tail } = splitPath(component.path));
+ }
+ let path: LocationDescriptor;
+ if (component.refKey) {
if (
- this.props.view === 'list' &&
- ['FIL', 'UTS', 'DIR'].includes(component.qualifier) &&
- component.path
+ !isPortfolioLike(component.qualifier) &&
+ ([MetricKey.releasability_rating, MetricKey.alert_status] as string[]).includes(metric.key)
) {
- ({ head, tail } = splitPath(component.path));
+ path = isApplication(component.qualifier)
+ ? getProjectUrl(component.refKey, component.branch)
+ : getBranchLikeUrl(component.refKey, branchLike);
+ } else if (isProject(component.qualifier) && metric.key === MetricKey.projects) {
+ path = getBranchLikeUrl(component.refKey, branchLike);
+ } else {
+ path = getComponentDrilldownUrlWithSelection(
+ component.refKey,
+ '',
+ metric.key,
+ branchLike,
+ view
+ );
}
-
- const isApp = this.props.rootComponent.qualifier === 'APP';
-
- return (
- <span title={componentKey}>
- <QualifierIcon className="little-spacer-right" qualifier={component.qualifier} />
- {head.length > 0 && <span className="note">{head}/</span>}
- <span>{tail}</span>
- {isApp &&
- (component.branch ? (
- <>
- <BranchIcon className="spacer-left little-spacer-right" />
- <span className="note">{component.branch}</span>
- </>
- ) : (
- <span className="spacer-left badge">{translate('branches.main_branch')}</span>
- ))}
- </span>
+ } else {
+ path = getComponentDrilldownUrlWithSelection(
+ rootComponent.key,
+ component.key,
+ metric.key,
+ branchLike,
+ view
);
}
- render() {
- const { branchLike, component, metric, rootComponent } = this.props;
-
- let hotspotsUrl;
- if (isFileType(component) && isSecurityReviewMetric(metric.key)) {
- hotspotsUrl = getComponentSecurityHotspotsUrl(this.props.rootComponent.key, {
- file: component.path,
- sinceLeakPeriod: isDiffMetric(metric.key) ? 'true' : undefined
- });
- }
-
- return (
- <td className="measure-details-component-cell">
- <div className="text-ellipsis">
- {!component.refKey ? (
- <Link
- className="link-no-underline"
- to={
- hotspotsUrl ||
- getComponentDrilldownUrlWithSelection(
- rootComponent.key,
- component.key,
- metric.key,
- branchLike
- )
- }
- id={'component-measures-component-link-' + component.key}
- onClick={hotspotsUrl ? undefined : this.handleClick}>
- {this.renderInner(component.key)}
- </Link>
- ) : (
- <Link
- className="link-no-underline"
- id={'component-measures-component-link-' + component.refKey}
- to={
- this.props.rootComponent.qualifier === 'APP'
- ? getProjectUrl(component.refKey, component.branch)
- : getBranchLikeUrl(component.refKey, branchLike)
- }>
- <span className="big-spacer-right">
- <LinkIcon />
- </span>
- {this.renderInner(component.refKey)}
- </Link>
+ return (
+ <td className="measure-details-component-cell">
+ <div className="text-ellipsis">
+ <Link
+ className="link-no-underline"
+ to={path}
+ id={'component-measures-component-link-' + component.key}>
+ {component.refKey && (
+ <span className="big-spacer-right">
+ <LinkIcon />
+ </span>
)}
- </div>
- </td>
- );
- }
+ <span title={component.key}>
+ <QualifierIcon className="little-spacer-right" qualifier={component.qualifier} />
+ {head.length > 0 && <span className="note">{head}/</span>}
+ <span>{tail}</span>
+ {isApplication(rootComponent.qualifier) &&
+ (component.branch ? (
+ <>
+ <BranchIcon className="spacer-left little-spacer-right" />
+ <span className="note">{component.branch}</span>
+ </>
+ ) : (
+ <span className="spacer-left badge">{translate('branches.main_branch')}</span>
+ ))}
+ </span>
+ </Link>
+ </div>
+ </td>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx
index 1bd16bccfbe..907f671a76f 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx
@@ -20,20 +20,19 @@
import * as React from 'react';
import { getLocalizedMetricName } from '../../../helpers/l10n';
import { BranchLike } from '../../../types/branch-like';
+import { MeasurePageView } from '../../../types/measures';
import { complementary } from '../config/complementary';
-import { View } from '../utils';
import ComponentsListRow from './ComponentsListRow';
import EmptyResult from './EmptyResult';
interface Props {
branchLike?: BranchLike;
components: T.ComponentMeasureEnhanced[];
- onClick: (component: string) => void;
metric: T.Metric;
metrics: T.Dict<T.Metric>;
rootComponent: T.ComponentMeasure;
selectedComponent?: string;
- view: View;
+ view: MeasurePageView;
}
export default function ComponentsList({ components, metric, metrics, ...props }: Props) {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx
index b1749e548f5..c29be3db951 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx
@@ -20,7 +20,7 @@
import * as classNames from 'classnames';
import * as React from 'react';
import { BranchLike } from '../../../types/branch-like';
-import { View } from '../utils';
+import { MeasurePageView } from '../../../types/measures';
import ComponentCell from './ComponentCell';
import MeasureCell from './MeasureCell';
@@ -28,11 +28,10 @@ interface Props {
branchLike?: BranchLike;
component: T.ComponentMeasureEnhanced;
isSelected: boolean;
- onClick: (component: string) => void;
otherMetrics: T.Metric[];
metric: T.Metric;
rootComponent: T.ComponentMeasure;
- view: View;
+ view: MeasurePageView;
}
export default function ComponentsListRow(props: Props) {
@@ -50,7 +49,6 @@ export default function ComponentsListRow(props: Props) {
branchLike={branchLike}
component={component}
metric={props.metric}
- onClick={props.onClick}
rootComponent={rootComponent}
view={props.view}
/>
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 828bad17a29..8e6271ea845 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
@@ -27,7 +27,7 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
import { formatMeasure, isDiffMetric, isPeriodBestValue } from '../../../helpers/measures';
import { scrollToElement } from '../../../helpers/scrolling';
import { BranchLike } from '../../../types/branch-like';
-import { View } from '../utils';
+import { MeasurePageView } from '../../../types/measures';
import ComponentsList from './ComponentsList';
interface Props {
@@ -44,7 +44,7 @@ interface Props {
rootComponent: T.ComponentMeasure;
selectedKey?: string;
selectedIdx?: number;
- view: View;
+ view: MeasurePageView;
}
interface State {
@@ -173,7 +173,6 @@ export default class FilesView extends React.PureComponent<Props, State> {
components={filteredComponents}
metric={this.props.metric}
metrics={this.props.metrics}
- onClick={this.props.handleOpen}
rootComponent={this.props.rootComponent}
selectedComponent={this.props.selectedKey}
view={this.props.view}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx
index 4673a98f943..45a67de0058 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx
@@ -21,12 +21,8 @@ import { shallow } from 'enzyme';
import { keyBy } from 'lodash';
import * as React from 'react';
import OriginalBubbleChart from '../../../../components/charts/BubbleChart';
-import {
- mockComponentMeasure,
- mockMeasure,
- mockMetric,
- mockPaging
-} from '../../../../helpers/testMocks';
+import { mockComponentMeasure } from '../../../../helpers/mocks/component';
+import { mockMeasure, mockMetric, mockPaging } from '../../../../helpers/testMocks';
import { MetricKey } from '../../../../types/metrics';
import { enhanceComponent } from '../../utils';
import BubbleChart from '../BubbleChart';
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx
index 981ef5a8bbe..ccb196e14b8 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx
@@ -19,31 +19,104 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockComponentMeasure, mockMetric } from '../../../../helpers/testMocks';
+import { mockBranch, mockPullRequest } from '../../../../helpers/mocks/branch-like';
+import {
+ mockComponentMeasure,
+ mockComponentMeasureEnhanced
+} from '../../../../helpers/mocks/component';
+import { mockMetric } from '../../../../helpers/testMocks';
+import { ComponentQualifier } from '../../../../types/component';
import { MetricKey } from '../../../../types/metrics';
import { enhanceComponent } from '../../utils';
-import ComponentCell from '../ComponentCell';
+import ComponentCell, { ComponentCellProps } from '../ComponentCell';
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({}, MetricKey.security_hotspots)).toMatchSnapshot('security review domain');
-
- const metric = mockMetric({ key: MetricKey.bugs });
expect(
shallowRender({
- component: enhanceComponent(
- mockComponentMeasure(false, { refKey: 'project-key' }),
- { key: metric.key },
- { [metric.key]: metric }
- )
+ rootComponent: mockComponentMeasure(false, { qualifier: ComponentQualifier.Application })
+ })
+ ).toMatchSnapshot('root component is application, component is on main branch');
+ expect(
+ shallowRender({
+ rootComponent: mockComponentMeasure(false, { qualifier: ComponentQualifier.Application }),
+ component: mockComponentMeasureEnhanced({ branch: 'develop' })
+ })
+ ).toMatchSnapshot('root component is application, component has branch');
+ expect(
+ shallowRender({ component: mockComponentMeasureEnhanced({ refKey: 'project-key' }) })
+ ).toMatchSnapshot('ref project component');
+ expect(
+ shallowRender(
+ {
+ component: mockComponentMeasureEnhanced({
+ refKey: 'project-key',
+ qualifier: ComponentQualifier.Project
+ }),
+ branchLike: mockBranch()
+ },
+ MetricKey.releasability_rating
+ )
+ ).toMatchSnapshot('ref project component, releasability metric');
+ expect(
+ shallowRender(
+ {
+ component: mockComponentMeasureEnhanced({
+ refKey: 'app-key',
+ qualifier: ComponentQualifier.Application
+ }),
+ branchLike: mockBranch()
+ },
+ MetricKey.projects
+ )
+ ).toMatchSnapshot('ref application component, projects');
+ expect(
+ shallowRender(
+ {
+ component: mockComponentMeasureEnhanced({
+ refKey: 'project-key',
+ qualifier: ComponentQualifier.Project
+ }),
+ branchLike: mockBranch()
+ },
+ MetricKey.projects
+ )
+ ).toMatchSnapshot('ref project component, projects');
+ expect(
+ shallowRender(
+ {
+ component: mockComponentMeasureEnhanced({
+ refKey: 'app-key',
+ qualifier: ComponentQualifier.Application
+ }),
+ branchLike: mockPullRequest()
+ },
+ MetricKey.alert_status
+ )
+ ).toMatchSnapshot('ref application component, alert_status metric');
+ expect(
+ shallowRender(
+ {
+ component: mockComponentMeasureEnhanced({
+ refKey: 'vw-key',
+ qualifier: ComponentQualifier.Portfolio
+ }),
+ branchLike: mockPullRequest()
+ },
+ MetricKey.alert_status
+ )
+ ).toMatchSnapshot('ref portfolio component, alert_status metric');
+ expect(
+ shallowRender({
+ component: mockComponentMeasureEnhanced({
+ key: 'svw-bar',
+ qualifier: ComponentQualifier.SubPortfolio
+ })
})
- ).toMatchSnapshot('ref component');
+ ).toMatchSnapshot('sub-portfolio component');
});
-function shallowRender(
- overrides: Partial<ComponentCell['props']> = {},
- metricKey = MetricKey.bugs
-) {
+function shallowRender(overrides: Partial<ComponentCellProps> = {}, metricKey = MetricKey.bugs) {
const metric = mockMetric({ key: metricKey });
const component = enhanceComponent(
mockComponentMeasure(true, {
@@ -53,11 +126,10 @@ function shallowRender(
{ [metric.key]: metric }
);
- return shallow<ComponentCell>(
+ return shallow<ComponentCellProps>(
<ComponentCell
component={component}
metric={metric}
- onClick={jest.fn()}
rootComponent={mockComponentMeasure(false)}
view="list"
{...overrides}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx
index bbefdc42480..478ab794bd0 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx
@@ -44,7 +44,6 @@ it('should renders correctly', () => {
components={COMPONENTS}
metric={METRICS.new_bugs}
metrics={METRICS}
- onClick={jest.fn()}
rootComponent={COMPONENTS[0]}
view="tree"
/>
@@ -59,7 +58,6 @@ it('should renders empty', () => {
components={[]}
metric={METRICS.new_bugs}
metrics={METRICS}
- onClick={jest.fn()}
rootComponent={COMPONENTS[0]}
view="tree"
/>
@@ -74,7 +72,6 @@ it('should renders with multiple measures', () => {
components={COMPONENTS}
metric={METRICS.coverage}
metrics={METRICS}
- onClick={jest.fn()}
rootComponent={COMPONENTS[0]}
view="tree"
/>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap
index 21d4106dec4..474ada97872 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap
@@ -10,7 +10,6 @@ exports[`should render correctly: default 1`] = `
<Link
className="link-no-underline"
id="component-measures-component-link-foo:src/index.tsx"
- onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
@@ -20,6 +19,7 @@ exports[`should render correctly: default 1`] = `
"id": "foo",
"metric": "bugs",
"selected": "foo:src/index.tsx",
+ "view": "list",
},
}
}
@@ -46,7 +46,7 @@ exports[`should render correctly: default 1`] = `
</td>
`;
-exports[`should render correctly: ref component 1`] = `
+exports[`should render correctly: ref application component, alert_status metric 1`] = `
<td
className="measure-details-component-cell"
>
@@ -55,7 +55,7 @@ exports[`should render correctly: ref component 1`] = `
>
<Link
className="link-no-underline"
- id="component-measures-component-link-project-key"
+ id="component-measures-component-link-foo"
onlyActiveOnIndex={false}
style={Object {}}
to={
@@ -63,7 +63,141 @@ exports[`should render correctly: ref component 1`] = `
"pathname": "/dashboard",
"query": Object {
"branch": undefined,
+ "id": "app-key",
+ },
+ }
+ }
+ >
+ <span
+ className="big-spacer-right"
+ >
+ <LinkIcon />
+ </span>
+ <span
+ title="foo"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ qualifier="APP"
+ />
+ <span>
+ Foo
+ </span>
+ </span>
+ </Link>
+ </div>
+</td>
+`;
+
+exports[`should render correctly: ref application component, projects 1`] = `
+<td
+ className="measure-details-component-cell"
+>
+ <div
+ className="text-ellipsis"
+ >
+ <Link
+ className="link-no-underline"
+ id="component-measures-component-link-foo"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/component_measures",
+ "query": Object {
+ "branch": "branch-6.7",
+ "id": "app-key",
+ "metric": "projects",
+ "view": "list",
+ },
+ }
+ }
+ >
+ <span
+ className="big-spacer-right"
+ >
+ <LinkIcon />
+ </span>
+ <span
+ title="foo"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ qualifier="APP"
+ />
+ <span>
+ Foo
+ </span>
+ </span>
+ </Link>
+ </div>
+</td>
+`;
+
+exports[`should render correctly: ref portfolio component, alert_status metric 1`] = `
+<td
+ className="measure-details-component-cell"
+>
+ <div
+ className="text-ellipsis"
+ >
+ <Link
+ className="link-no-underline"
+ id="component-measures-component-link-foo"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/component_measures",
+ "query": Object {
+ "id": "vw-key",
+ "metric": "alert_status",
+ "pullRequest": "1001",
+ "view": "list",
+ },
+ }
+ }
+ >
+ <span
+ className="big-spacer-right"
+ >
+ <LinkIcon />
+ </span>
+ <span
+ title="foo"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ qualifier="VW"
+ />
+ <span>
+ Foo
+ </span>
+ </span>
+ </Link>
+ </div>
+</td>
+`;
+
+exports[`should render correctly: ref project component 1`] = `
+<td
+ className="measure-details-component-cell"
+>
+ <div
+ className="text-ellipsis"
+ >
+ <Link
+ className="link-no-underline"
+ id="component-measures-component-link-foo"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/component_measures",
+ "query": Object {
"id": "project-key",
+ "metric": "bugs",
+ "view": "list",
},
}
}
@@ -74,7 +208,7 @@ exports[`should render correctly: ref component 1`] = `
<LinkIcon />
</span>
<span
- title="project-key"
+ title="foo"
>
<QualifierIcon
className="little-spacer-right"
@@ -89,7 +223,141 @@ exports[`should render correctly: ref component 1`] = `
</td>
`;
-exports[`should render correctly: security review domain 1`] = `
+exports[`should render correctly: ref project component, projects 1`] = `
+<td
+ className="measure-details-component-cell"
+>
+ <div
+ className="text-ellipsis"
+ >
+ <Link
+ className="link-no-underline"
+ id="component-measures-component-link-foo"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": "branch-6.7",
+ "id": "project-key",
+ },
+ }
+ }
+ >
+ <span
+ className="big-spacer-right"
+ >
+ <LinkIcon />
+ </span>
+ <span
+ title="foo"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ qualifier="TRK"
+ />
+ <span>
+ Foo
+ </span>
+ </span>
+ </Link>
+ </div>
+</td>
+`;
+
+exports[`should render correctly: ref project component, releasability metric 1`] = `
+<td
+ className="measure-details-component-cell"
+>
+ <div
+ className="text-ellipsis"
+ >
+ <Link
+ className="link-no-underline"
+ id="component-measures-component-link-foo"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": "branch-6.7",
+ "id": "project-key",
+ },
+ }
+ }
+ >
+ <span
+ className="big-spacer-right"
+ >
+ <LinkIcon />
+ </span>
+ <span
+ title="foo"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ qualifier="TRK"
+ />
+ <span>
+ Foo
+ </span>
+ </span>
+ </Link>
+ </div>
+</td>
+`;
+
+exports[`should render correctly: root component is application, component has branch 1`] = `
+<td
+ className="measure-details-component-cell"
+>
+ <div
+ className="text-ellipsis"
+ >
+ <Link
+ className="link-no-underline"
+ id="component-measures-component-link-foo"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/component_measures",
+ "query": Object {
+ "id": "foo",
+ "metric": "bugs",
+ "selected": "foo",
+ "view": "list",
+ },
+ }
+ }
+ >
+ <span
+ title="foo"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ qualifier="TRK"
+ />
+ <span>
+ Foo
+ </span>
+ <BranchIcon
+ className="spacer-left little-spacer-right"
+ />
+ <span
+ className="note"
+ >
+ develop
+ </span>
+ </span>
+ </Link>
+ </div>
+</td>
+`;
+
+exports[`should render correctly: root component is application, component is on main branch 1`] = `
<td
className="measure-details-component-cell"
>
@@ -103,15 +371,12 @@ exports[`should render correctly: security review domain 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/security_hotspots",
+ "pathname": "/component_measures",
"query": Object {
- "assignedToMe": undefined,
- "branch": undefined,
- "file": "src/index.tsx",
- "hotspots": undefined,
"id": "foo",
- "pullRequest": undefined,
- "sinceLeakPeriod": undefined,
+ "metric": "bugs",
+ "selected": "foo:src/index.tsx",
+ "view": "list",
},
}
}
@@ -132,6 +397,51 @@ exports[`should render correctly: security review domain 1`] = `
<span>
index.tsx
</span>
+ <span
+ className="spacer-left badge"
+ >
+ branches.main_branch
+ </span>
+ </span>
+ </Link>
+ </div>
+</td>
+`;
+
+exports[`should render correctly: sub-portfolio component 1`] = `
+<td
+ className="measure-details-component-cell"
+>
+ <div
+ className="text-ellipsis"
+ >
+ <Link
+ className="link-no-underline"
+ id="component-measures-component-link-svw-bar"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/component_measures",
+ "query": Object {
+ "id": "foo",
+ "metric": "bugs",
+ "selected": "svw-bar",
+ "view": "list",
+ },
+ }
+ }
+ >
+ <span
+ title="svw-bar"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ qualifier="SVW"
+ />
+ <span>
+ Foo
+ </span>
</span>
</Link>
</div>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap
index eee4db08dc2..85f717ade47 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap
@@ -24,7 +24,6 @@ exports[`should renders correctly 1`] = `
"type": "INT",
}
}
- onClick={[MockFunction]}
otherMetrics={Array []}
rootComponent={
Object {
@@ -102,7 +101,6 @@ exports[`should renders with multiple measures 1`] = `
"type": "PERCENT",
}
}
- onClick={[MockFunction]}
otherMetrics={
Array [
Object {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap
index edf84eb575e..75f52aa9974 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap
@@ -31,7 +31,6 @@ exports[`should render with best values hidden 1`] = `
},
}
}
- onClick={[MockFunction]}
rootComponent={
Object {
"key": "parent",
@@ -92,7 +91,6 @@ exports[`should renders correctly 1`] = `
},
}
}
- onClick={[MockFunction]}
rootComponent={
Object {
"key": "parent",
diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.ts b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
index a36aaf2a613..5b95979165e 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/utils.ts
+++ b/server/sonar-web/src/main/js/apps/component-measures/utils.ts
@@ -25,15 +25,14 @@ import { getDisplayMetrics, isDiffMetric } from '../../helpers/measures';
import { cleanQuery, parseAsString, serializeString } from '../../helpers/query';
import { BranchLike } from '../../types/branch-like';
import { ComponentQualifier } from '../../types/component';
+import { MeasurePageView } from '../../types/measures';
import { MetricKey } from '../../types/metrics';
import { bubbles } from './config/bubbles';
import { domains } from './config/domains';
-export type View = 'list' | 'tree' | 'treemap';
-
export const BUBBLES_FETCH_LIMIT = 500;
export const PROJECT_OVERVEW = 'project_overview';
-export const DEFAULT_VIEW: View = 'tree';
+export const DEFAULT_VIEW: MeasurePageView = 'tree';
export const DEFAULT_METRIC = PROJECT_OVERVEW;
export const KNOWN_DOMAINS = [
'Releasability',
@@ -212,8 +211,8 @@ export function isProjectOverview(metric: string) {
return metric === PROJECT_OVERVEW;
}
-function parseView(metric: string, rawView?: string): View {
- const view = (parseAsString(rawView) || DEFAULT_VIEW) as View;
+function parseView(metric: string, rawView?: string): MeasurePageView {
+ const view = (parseAsString(rawView) || DEFAULT_VIEW) as MeasurePageView;
if (!hasTree(metric)) {
return 'list';
} else if (view === 'list' && !hasList(metric)) {
@@ -225,7 +224,7 @@ function parseView(metric: string, rawView?: string): View {
export interface Query {
metric: string;
selected?: string;
- view: View;
+ view: MeasurePageView;
}
export const parseQuery = memoize(