metric={metric}
metrics={this.props.metrics}
paging={this.state.paging}
- selectedKey={selectedIdx != null ? this.state.selected : null}
+ rootComponent={this.props.rootComponent}
selectedIdx={selectedIdx}
+ selectedKey={selectedIdx != null ? this.state.selected : null}
/>
);
}
import LinkIcon from '../../../components/icons-components/LinkIcon';
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import { splitPath } from '../../../helpers/path';
-import { getPathUrlAsString, getBranchLikeUrl } from '../../../helpers/urls';
-/*:: import type { ComponentEnhanced } from '../types'; */
+import { getBranchLikeUrl, getComponentDrilldownUrlWithSelection } from '../../../helpers/urls';
+/*:: import type { Component, ComponentEnhanced } from '../types'; */
+/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {
branchLike?: { id?: string; name: string },
component: ComponentEnhanced,
- onClick: string => void
+ onClick: string => void,
+ metric: Metric,
+ rootComponent: Component
}; */
export default class ComponentCell extends React.PureComponent {
/*:: props: Props; */
- handleClick = (e /*: MouseEvent */) => {
- const isLeftClickEvent = e.button === 0;
- const isModifiedEvent = !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
-
- if (isLeftClickEvent && !isModifiedEvent) {
- e.preventDefault();
- this.props.onClick(this.props.component.key);
- }
- };
-
renderInner() {
const { component } = this.props;
let head = '';
}
render() {
- const { branchLike, component } = this.props;
+ const { branchLike, component, metric, rootComponent } = this.props;
return (
<td className="measure-details-component-cell">
<div className="text-ellipsis">
- {/* TODO make this <a> link a react-router <Link /> */}
{component.refKey == null ? (
- <a
- id={'component-measures-component-link-' + component.key}
+ <Link
className="link-no-underline"
- href={getPathUrlAsString(getBranchLikeUrl(component.key, branchLike))}
- onClick={this.handleClick}>
+ id={'component-measures-component-link-' + component.key}
+ to={getComponentDrilldownUrlWithSelection(
+ rootComponent.key,
+ component.key,
+ metric.key,
+ branchLike
+ )}>
{this.renderInner()}
- </a>
+ </Link>
) : (
<Link
className="link-no-underline"
import EmptyResult from './EmptyResult';
import { complementary } from '../config/complementary';
import { getLocalizedMetricName } from '../../../helpers/l10n';
-/*:: import type { ComponentEnhanced } from '../types'; */
+/*:: import type { Component, ComponentEnhanced } from '../types'; */
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
onClick: string => void,
metric: Metric,
metrics: { [string]: Metric },
+ rootComponent: Component,
selectedComponent?: ?string
|}; */
export default function ComponentsList(
- { branchLike, components, onClick, metrics, metric, selectedComponent } /*: Props */
+ {
+ branchLike,
+ components,
+ onClick,
+ metrics,
+ metric,
+ rootComponent,
+ selectedComponent
+ } /*: Props */
) {
if (!components.length) {
return <EmptyResult />;
<span className="small">{getLocalizedMetricName(metric)}</span>
</th>
{otherMetrics.map(metric => (
- <th key={metric.key} className="text-right">
+ <th className="text-right" key={metric.key}>
<span className="small">{getLocalizedMetricName(metric)}</span>
</th>
))}
<tbody>
{components.map(component => (
<ComponentsListRow
- key={component.id}
branchLike={branchLike}
component={component}
- otherMetrics={otherMetrics}
isSelected={component.key === selectedComponent}
+ key={component.id}
metric={metric}
onClick={onClick}
+ otherMetrics={otherMetrics}
+ rootComponent={rootComponent}
/>
))}
</tbody>
import classNames from 'classnames';
import ComponentCell from './ComponentCell';
import MeasureCell from './MeasureCell';
-/*:: import type { ComponentEnhanced } from '../types'; */
+/*:: import type { Component, ComponentEnhanced } from '../types'; */
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
isSelected: boolean,
onClick: string => void,
otherMetrics: Array<Metric>,
- metric: Metric
+ metric: Metric,
+ rootComponent: Component
|}; */
export default function ComponentsListRow(props /*: Props */) {
- const { branchLike, component } = props;
+ const { branchLike, component, rootComponent } = props;
const otherMeasures = props.otherMetrics.map(metric => {
const measure = component.measures.find(measure => measure.metric.key === metric.key);
return { ...measure, metric };
});
return (
<tr className={rowClass}>
- <ComponentCell branchLike={branchLike} component={component} onClick={props.onClick} />
+ <ComponentCell
+ branchLike={branchLike}
+ component={component}
+ metric={props.metric}
+ onClick={props.onClick}
+ rootComponent={rootComponent}
+ />
<MeasureCell component={component} metric={props.metric} />
{otherMeasures.map(measure => (
<MeasureCell
- key={measure.metric.key}
component={component}
+ key={measure.metric.key}
measure={measure}
metric={measure.metric}
/>
import ComponentsList from './ComponentsList';
import ListFooter from '../../../components/controls/ListFooter';
import { scrollToElement } from '../../../helpers/scrolling';
-/*:: import type { ComponentEnhanced, Paging } from '../types'; */
+/*:: import type { Component, ComponentEnhanced, Paging } from '../types'; */
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
metric: Metric,
metrics: { [string]: Metric },
paging: ?Paging,
+ rootComponent: Component,
selectedKey: ?string,
selectedIdx: ?number
|}; */
<ComponentsList
branchLike={this.props.branchLike}
components={this.props.components}
- metrics={this.props.metrics}
metric={this.props.metric}
+ metrics={this.props.metrics}
onClick={this.props.handleOpen}
+ rootComponent={this.props.rootComponent}
selectedComponent={this.props.selectedKey}
/>
{this.props.paging &&
this.props.components.length > 0 && (
<ListFooter
count={this.props.components.length}
- total={this.props.paging.total}
loadMore={this.props.fetchMore}
+ total={this.props.paging.total}
/>
)}
</div>
<span>{translate('metric.bugs.name')}</span>
<Link
className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, 'Reliability', branchLike)}>
+ to={getComponentDrilldownUrl({
+ componentKey: component.key,
+ metric: 'Reliability',
+ branchLike
+ })}>
<BubblesIcon size={14} />
</Link>
<span className="big-spacer-left">{translate('metric.vulnerabilities.name')}</span>
<Link
className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, 'Security', branchLike)}>
+ to={getComponentDrilldownUrl({
+ componentKey: component.key,
+ metric: 'Security',
+ branchLike
+ })}>
<BubblesIcon size={14} />
</Link>
</div>
<span>{label}</span>
<Link
className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, domain, branchLike)}>
+ to={getComponentDrilldownUrl({
+ componentKey: component.key,
+ metric: domain,
+ branchLike
+ })}>
<BubblesIcon size={14} />
</Link>
</div>
id="portfolio.x_in_y"
values={{
projects: (
- <Link to={getComponentDrilldownUrl(component, metricKey)}>
+ <Link to={getComponentDrilldownUrl({ componentKey: component, metric: metricKey })}>
<span>
<Measure
className="little-spacer-right"
return (
<Link
className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component, metric)}>
+ to={getComponentDrilldownUrl({ componentKey: component, metric })}>
<BubblesIcon size={14} />
</Link>
);
{rating && (
<Link
- to={getComponentDrilldownUrl(component, 'alert_status')}
+ to={getComponentDrilldownUrl({ componentKey: component, metric: 'alert_status' })}
className="portfolio-box-rating">
<Rating value={rating} />
</Link>
{effort &&
Number(effort) > 0 && (
<div className="portfolio-effort">
- <Link to={getComponentDrilldownUrl(component, 'alert_status')}>
+ <Link
+ to={getComponentDrilldownUrl({ componentKey: component, metric: 'alert_status' })}>
<span>
<Measure
className="little-spacer-right"
<li>
<div className="portfolio-measure-secondary-value">
{projects ? (
- <Link to={getComponentDrilldownUrl(component.key, 'projects')}>
+ <Link
+ to={getComponentDrilldownUrl({ componentKey: component.key, metric: 'projects' })}>
<Measure metricKey="projects" metricType="SHORT_INT" value={projects} />
</Link>
) : (
<li>
<div className="portfolio-measure-secondary-value">
{ncloc ? (
- <Link to={getComponentDrilldownUrl(component.key, 'ncloc')}>
+ <Link to={getComponentDrilldownUrl({ componentKey: component.key, metric: 'ncloc' })}>
<Measure metricKey="ncloc" metricType="SHORT_INT" value={ncloc} />
</Link>
) : (
return this.renderIssuesLink();
}
- const url = getComponentDrilldownUrl(
- this.props.component,
- this.props.metric,
- this.props.branchLike
- );
+ const url = getComponentDrilldownUrl({
+ componentKey: this.props.component,
+ metric: this.props.metric,
+ branchLike: this.props.branchLike
+ });
return (
- <Link to={url} className={this.props.className}>
+ <Link className={this.props.className} to={url}>
{this.props.children}
</Link>
);
describe('#getComponentDrilldownUrl', () => {
it('should return component drilldown url', () => {
- expect(getComponentDrilldownUrl(SIMPLE_COMPONENT_KEY, METRIC)).toEqual({
+ expect(
+ getComponentDrilldownUrl({ componentKey: SIMPLE_COMPONENT_KEY, metric: METRIC })
+ ).toEqual({
pathname: '/component_measures',
query: { id: SIMPLE_COMPONENT_KEY, metric: METRIC }
});
});
it('should not encode component key', () => {
- expect(getComponentDrilldownUrl(COMPLEX_COMPONENT_KEY, METRIC)).toEqual({
+ expect(
+ getComponentDrilldownUrl({ componentKey: COMPLEX_COMPONENT_KEY, metric: METRIC })
+ ).toEqual({
pathname: '/component_measures',
query: { id: COMPLEX_COMPONENT_KEY, metric: METRIC }
});
/**
* Generate URL for a component's drilldown page
*/
-export function getComponentDrilldownUrl(
+export function getComponentDrilldownUrl(options: {
+ componentKey: string;
+ metric: string;
+ branchLike?: BranchLike;
+ selectionKey?: string;
+ treemapView?: boolean;
+}): Location {
+ const { componentKey, metric, branchLike, selectionKey, treemapView } = options;
+ const query: Query = { id: componentKey, metric, ...getBranchLikeQuery(branchLike) };
+ if (treemapView) {
+ query.view = 'treemap';
+ }
+ if (selectionKey) {
+ query.selected = selectionKey;
+ }
+ return { pathname: '/component_measures', query };
+}
+
+export function getComponentDrilldownUrlWithSelection(
componentKey: string,
+ selectionKey: string,
metric: string,
branchLike?: BranchLike
): Location {
- return {
- pathname: '/component_measures',
- query: { id: componentKey, metric, ...getBranchLikeQuery(branchLike) }
- };
+ return getComponentDrilldownUrl({ componentKey, selectionKey, metric, branchLike });
}
-export function getMeasureTreemapUrl(component: string, metric: string) {
- return {
- pathname: '/component_measures',
- query: { id: component, metric, view: 'treemap' }
- };
+export function getMeasureTreemapUrl(componentKey: string, metric: string) {
+ return getComponentDrilldownUrl({ componentKey, metric, treemapView: true });
}
export function getActivityUrl(component: string, branchLike?: BranchLike) {