]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9843 Open measure page in blank page when middle click on a file
authorPascal Mugnier <pascal.mugnier@sonarsource.com>
Tue, 17 Apr 2018 07:18:52 +0000 (09:18 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 18 Apr 2018 18:20:53 +0000 (20:20 +0200)
14 files changed:
server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.js
server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx
server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
server/sonar-web/src/main/js/apps/portfolio/components/Effort.tsx
server/sonar-web/src/main/js/apps/portfolio/components/MeasuresButtonLink.tsx
server/sonar-web/src/main/js/apps/portfolio/components/ReleasabilityBox.tsx
server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx
server/sonar-web/src/main/js/components/shared/DrilldownLink.tsx
server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts
server/sonar-web/src/main/js/helpers/urls.ts

index f18f16b55e207de7ec45d9bee50dba24132beddb..a4847ed1848d42a47ec301ce3b797d84ccc70e94 100644 (file)
@@ -253,8 +253,9 @@ export default class MeasureContent extends React.PureComponent {
             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}
           />
         );
       }
index 248b6469e2bbf3500aa6558c2622019b7d8e8dfc..25b6b7b4f107de3d2b66c634b677b353afd1a984 100644 (file)
@@ -23,28 +23,21 @@ import { Link } from 'react-router';
 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 = '';
@@ -65,19 +58,22 @@ export default class ComponentCell extends React.PureComponent {
   }
 
   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"
index 0e29a704f0c4a37de9d2550ec95f3245b4c0c2df..cfc0aa28aa253fc2c0254588e7730a7d82f2660c 100644 (file)
@@ -23,7 +23,7 @@ import ComponentsListRow from './ComponentsListRow';
 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 = {|
@@ -32,11 +32,20 @@ import { getLocalizedMetricName } from '../../../helpers/l10n';
   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 />;
@@ -53,7 +62,7 @@ export default function ComponentsList(
               <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>
             ))}
@@ -64,13 +73,14 @@ export default function ComponentsList(
       <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>
index 47a5d2bf29a85a9d254508cafed6eecb5361508b..891bd5b574746b3e8de87a1a2b513ddaa744ee87 100644 (file)
@@ -22,7 +22,7 @@ import React from 'react';
 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 = {|
@@ -31,11 +31,12 @@ import MeasureCell from './MeasureCell';
   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 };
@@ -45,14 +46,20 @@ export default function ComponentsListRow(props /*: Props */) {
   });
   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}
         />
index 2fd8d7feb27560911b41b2a5ca77d7fd93ec6019..4c1fbb8ce082b18f4b1b1e7e2388d3dec176334f 100644 (file)
@@ -24,7 +24,7 @@ import { throttle } from 'lodash';
 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 = {|
@@ -36,6 +36,7 @@ import { scrollToElement } from '../../../helpers/scrolling';
   metric: Metric,
   metrics: { [string]: Metric },
   paging: ?Paging,
+  rootComponent: Component,
   selectedKey: ?string,
   selectedIdx: ?number
 |}; */
@@ -125,17 +126,18 @@ export default class ListView extends React.PureComponent {
         <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>
index 52fd2d5253b152e6465aa1c46c0857ef48b96a93..3ce147ba7185f31567ffc9ec3f0ed58ab6b866df 100644 (file)
@@ -39,13 +39,21 @@ export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
           <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>
index 9797387082db61ef2240b658400f2f3c88c81b47..2ff27976bf8660d7ae029b0367d18f304a510979 100644 (file)
@@ -85,7 +85,11 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
             <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>
index 4029dbe7a383a1fa26f7f8a15cfc0eda92d58ddf..d2f0235d4551b65f673fcbc88353719ccceaceec 100644 (file)
@@ -39,7 +39,7 @@ export default function Effort({ component, effort, metricKey }: Props) {
         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"
index 8cb9d28bc76517525eb2cb18d3180881250d5150..9f90824d2b86eeb5cf4ff189a083898c76888bcc 100644 (file)
@@ -31,7 +31,7 @@ export default function MeasuresButtonLink({ component, metric }: Props) {
   return (
     <Link
       className="button button-small spacer-left text-text-bottom"
-      to={getComponentDrilldownUrl(component, metric)}>
+      to={getComponentDrilldownUrl({ componentKey: component, metric })}>
       <BubblesIcon size={14} />
     </Link>
   );
index a33d40f9ef17f6dd1475d8467e1156ed02984f7a..3dd30c8354176e2548efefabe2dcbb6764cc6880 100644 (file)
@@ -41,7 +41,7 @@ export default function ReleasabilityBox({ component, measures }: Props) {
 
       {rating && (
         <Link
-          to={getComponentDrilldownUrl(component, 'alert_status')}
+          to={getComponentDrilldownUrl({ componentKey: component, metric: 'alert_status' })}
           className="portfolio-box-rating">
           <Rating value={rating} />
         </Link>
@@ -52,7 +52,8 @@ export default function ReleasabilityBox({ component, measures }: Props) {
       {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"
index 7d438b6167f3cb82eaccf1a3f91142e3c521509d..3f3ed3e1253bded3483a99b3cd1cb03ef51f5152 100644 (file)
@@ -41,7 +41,8 @@ export default function Summary({ component, measures }: Props) {
         <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>
             ) : (
@@ -53,7 +54,7 @@ export default function Summary({ component, measures }: Props) {
         <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>
             ) : (
index ff5587d95b4cb9bdc7786408033ce905d948d77f..8836780c7f9ea299f58ac541fb7a77501976eaa8 100644 (file)
@@ -138,13 +138,13 @@ export default class DrilldownLink extends React.PureComponent<Props> {
       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>
     );
index 75382073d329a0385d065d7d2b509280f3cb0fb1..bb637690f4c67010bfa72e61d6a9994c10284ff6 100644 (file)
@@ -81,14 +81,18 @@ describe('#getComponentIssuesUrl', () => {
 
 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 }
     });
index 20cec1b04fb1ee79b97d1a7ab4969397af370cd6..a04d8376536fb49e5fcf6bbef17fd26913640c24 100644 (file)
@@ -103,22 +103,35 @@ export function getComponentIssuesUrl(componentKey: string, query?: Query): Loca
 /**
  * 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) {