]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7144 pin file in the workspace
authorStas Vilchik <vilchiks@gmail.com>
Thu, 7 Jan 2016 16:48:53 +0000 (17:48 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Thu, 7 Jan 2016 16:52:22 +0000 (17:52 +0100)
server/sonar-web/src/main/js/apps/code/components/Component.js
server/sonar-web/src/main/js/apps/code/components/ComponentDetach.js
server/sonar-web/src/main/js/apps/code/components/ComponentPin.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/shared/pin-icon.js [new file with mode: 0644]
server/sonar-web/tests/apps/code/components-test.js
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index de9a7e1055f3a04f09ce774c0d425f9586717217..205af9cef65d362ae40ad465ec90ea70afd05ae9 100644 (file)
@@ -22,63 +22,77 @@ import React from 'react';
 import ComponentName from './ComponentName';
 import ComponentMeasure from './ComponentMeasure';
 import ComponentDetach from './ComponentDetach';
+import ComponentPin from './ComponentPin';
 
 
-const Component = ({ component, previous, coverageMetric, onBrowse }) => (
-    <tr>
-      <td className="thin nowrap">
-        <span className="spacer-right">
-          <ComponentDetach component={component}/>
-        </span>
-      </td>
-      <td className="code-name-cell">
-        <ComponentName
-            component={component}
-            previous={previous}
-            onBrowse={onBrowse}/>
-      </td>
-      <td className="thin nowrap text-right">
-        <div className="code-components-cell">
-          <ComponentMeasure
-              component={component}
-              metricKey="ncloc"
-              metricType="SHORT_INT"/>
-        </div>
-      </td>
-      <td className="thin nowrap text-right">
-        <div className="code-components-cell">
-          <ComponentMeasure
-              component={component}
-              metricKey="sqale_index"
-              metricType="SHORT_WORK_DUR"/>
-        </div>
-      </td>
-      <td className="thin nowrap text-right">
-        <div className="code-components-cell">
-          <ComponentMeasure
-              component={component}
-              metricKey="violations"
-              metricType="SHORT_INT"/>
-        </div>
-      </td>
-      <td className="thin nowrap text-right">
-        <div className="code-components-cell">
-          <ComponentMeasure
-              component={component}
-              metricKey={coverageMetric}
-              metricType="PERCENT"/>
-        </div>
-      </td>
-      <td className="thin nowrap text-right">
-        <div className="code-components-cell">
-          <ComponentMeasure
+const Component = ({ component, previous, coverageMetric, onBrowse }) => {
+  let componentAction = null;
+
+  switch (component.qualifier) {
+    case 'FIL':
+    case 'UTS':
+      componentAction = <ComponentPin component={component}/>;
+      break;
+    default:
+      componentAction = <ComponentDetach component={component}/>;
+  }
+
+  return (
+      <tr>
+        <td className="thin nowrap">
+          <span className="spacer-right">
+            {componentAction}
+          </span>
+        </td>
+        <td className="code-name-cell">
+          <ComponentName
               component={component}
-              metricKey="duplicated_lines_density"
-              metricType="PERCENT"/>
-        </div>
-      </td>
-    </tr>
-);
+              previous={previous}
+              onBrowse={onBrowse}/>
+        </td>
+        <td className="thin nowrap text-right">
+          <div className="code-components-cell">
+            <ComponentMeasure
+                component={component}
+                metricKey="ncloc"
+                metricType="SHORT_INT"/>
+          </div>
+        </td>
+        <td className="thin nowrap text-right">
+          <div className="code-components-cell">
+            <ComponentMeasure
+                component={component}
+                metricKey="sqale_index"
+                metricType="SHORT_WORK_DUR"/>
+          </div>
+        </td>
+        <td className="thin nowrap text-right">
+          <div className="code-components-cell">
+            <ComponentMeasure
+                component={component}
+                metricKey="violations"
+                metricType="SHORT_INT"/>
+          </div>
+        </td>
+        <td className="thin nowrap text-right">
+          <div className="code-components-cell">
+            <ComponentMeasure
+                component={component}
+                metricKey={coverageMetric}
+                metricType="PERCENT"/>
+          </div>
+        </td>
+        <td className="thin nowrap text-right">
+          <div className="code-components-cell">
+            <ComponentMeasure
+                component={component}
+                metricKey="duplicated_lines_density"
+                metricType="PERCENT"/>
+          </div>
+        </td>
+      </tr>
+  );
+};
 
 
 export default Component;
index 37d6f62dc4e2f3b5c7049ea9b886141bf318d544..b686188538a098fcab695fc3f39a0b091ea4959f 100644 (file)
@@ -26,8 +26,7 @@ import { translate } from '../../../helpers/l10n';
 const ComponentDetach = ({ component }) => (
     <a
         className="icon-detach"
-        target="_blank"
-        title={translate('code.open_in_new_tab')}
+        title={translate('code.open_component_page')}
         href={getComponentUrl(component.key)}/>
 );
 
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.js b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.js
new file mode 100644 (file)
index 0000000..8038273
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube :: Web
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 React from 'react';
+
+import Workspace from '../../../components/workspace/main';
+import PinIcon from '../../../components/shared/pin-icon';
+import { translate } from '../../../helpers/l10n';
+
+
+const ComponentPin = ({ component }) => {
+  const handleClick = (e) => {
+    e.preventDefault();
+    Workspace.openComponent({ uuid: component.uuid });
+  };
+
+  return (
+      <a
+          className="link-no-underline"
+          onClick={handleClick}
+          title={translate('component_viewer.open_in_workspace')}
+          href="#">
+        <PinIcon/>
+      </a>
+  );
+};
+
+
+export default ComponentPin;
diff --git a/server/sonar-web/src/main/js/components/shared/pin-icon.js b/server/sonar-web/src/main/js/components/shared/pin-icon.js
new file mode 100644 (file)
index 0000000..d6b26a8
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube :: Web
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 React from 'react';
+
+const PinIcon = () => (
+    <svg width="9" height="14" viewBox="0 0 288 448">
+      <path
+          fill="#236a97"
+          d="M120 216v-112q0-3.5-2.25-5.75t-5.75-2.25-5.75 2.25-2.25 5.75v112q0 3.5 2.25 5.75t5.75 2.25 5.75-2.25 2.25-5.75zM288 304q0 6.5-4.75 11.25t-11.25 4.75h-107.25l-12.75 120.75q-0.5 3-2.625 5.125t-5.125 2.125h-0.25q-6.75 0-8-6.75l-19-121.25h-101q-6.5 0-11.25-4.75t-4.75-11.25q0-30.75 19.625-55.375t44.375-24.625v-128q-13 0-22.5-9.5t-9.5-22.5 9.5-22.5 22.5-9.5h160q13 0 22.5 9.5t9.5 22.5-9.5 22.5-22.5 9.5v128q24.75 0 44.375 24.625t19.625 55.375z"/>
+    </svg>
+);
+
+export default PinIcon;
+
index 7729bf7b5ab2129eb2936226ddd72f98316d20b6..a31591cf24ea852d655d092fbd507d7426f3ef7c 100644 (file)
@@ -7,11 +7,9 @@ import TestUtils from 'react-addons-test-utils';
 
 import Breadcrumb from '../../../src/main/js/apps/code/components/Breadcrumb';
 import Breadcrumbs from '../../../src/main/js/apps/code/components/Breadcrumbs';
-import Component from '../../../src/main/js/apps/code/components/Component';
 import ComponentDetach from '../../../src/main/js/apps/code/components/ComponentDetach';
 import ComponentMeasure from '../../../src/main/js/apps/code/components/ComponentMeasure';
 import ComponentName from '../../../src/main/js/apps/code/components/ComponentName';
-import Components from '../../../src/main/js/apps/code/components/Components';
 import ComponentsEmpty from '../../../src/main/js/apps/code/components/ComponentsEmpty';
 import Truncated from '../../../src/main/js/apps/code/components/Truncated';
 
@@ -87,50 +85,6 @@ describe('Code :: Components', () => {
     });
   });
 
-  describe('<Component/>', () => {
-    let output;
-
-    before(() => {
-      output = shallow(
-          <Component
-              component={exampleComponent}
-              coverageMetric="coverage"
-              onBrowse={exampleOnBrowse}/>);
-    });
-
-    it('should render <ComponentName/>', () => {
-      const findings = output.find(ComponentName);
-      expect(findings)
-          .to.have.length(1);
-      expect(findings.first().props())
-          .to.deep.equal({ component: exampleComponent, previous: undefined, onBrowse: exampleOnBrowse });
-    });
-
-    it('should render <ComponentMeasure/>s', () => {
-      const findings = output.find(ComponentMeasure);
-      expect(findings)
-          .to.have.length(5);
-      expect(findings.at(0).props())
-          .to.deep.equal({ component: exampleComponent, metricKey: 'ncloc', metricType: 'SHORT_INT' });
-      expect(findings.at(1).props())
-          .to.deep.equal({ component: exampleComponent, metricKey: 'sqale_index', metricType: 'SHORT_WORK_DUR' });
-      expect(findings.at(2).props())
-          .to.deep.equal({ component: exampleComponent, metricKey: 'violations', metricType: 'SHORT_INT' });
-      expect(findings.at(3).props())
-          .to.deep.equal({ component: exampleComponent, metricKey: 'coverage', metricType: 'PERCENT' });
-      expect(findings.at(4).props())
-          .to.deep.equal({ component: exampleComponent, metricKey: 'duplicated_lines_density', metricType: 'PERCENT' });
-    });
-
-    it('should render <ComponentDetach/>', () => {
-      const findings = output.find(ComponentDetach);
-      expect(findings)
-          .to.have.length(1);
-      expect(findings.first().props())
-          .to.deep.equal({ component: exampleComponent });
-    });
-  });
-
   describe('<ComponentDetach/>', () => {
     it('should render link', () => {
       const output = shallow(
@@ -139,8 +93,6 @@ describe('Code :: Components', () => {
 
       expect(output.type())
           .to.equal('a');
-      expect(output.prop('target'))
-          .to.equal('_blank');
       expect(output.prop('href'))
           .to.equal(expectedUrl);
     });
@@ -228,40 +180,6 @@ describe('Code :: Components', () => {
     });
   });
 
-  describe('<Components/>', () => {
-    let output;
-
-    before(() => {
-      output = shallow(
-          <Components
-              baseComponent={exampleComponent}
-              components={[exampleComponent2, exampleComponent3]}
-              onBrowse={exampleOnBrowse}/>);
-    });
-
-    it('should render base component', () => {
-      const findings = output.findWhere(node => {
-        return node.type() === Component && node.prop('component') === exampleComponent;
-      });
-
-      expect(findings)
-          .to.have.length(1);
-      expect(findings.first().prop('onBrowse'))
-          .to.not.be.ok;
-    });
-
-    it('should render children component', () => {
-      const findings = output.findWhere(node => {
-        return node.type() === Component && node.prop('component') !== exampleComponent;
-      });
-
-      expect(findings)
-          .to.have.length(2);
-      expect(findings.at(0).prop('onBrowse'))
-          .to.equal(exampleOnBrowse)
-    });
-  });
-
   describe('<ComponentsEmpty/>', () => {
     it('should render', () => {
       const output = shallow(<ComponentsEmpty/>);
index 8f4abb3efaeb89b761decadf6c62dd3c6e0e01bc..22198010256919697af51639fa487eb0c38858e5 100644 (file)
@@ -3170,4 +3170,4 @@ api_documentation.internal_tooltip=Use at your own risk; internal services are s
 # CODE
 #
 #------------------------------------------------------------------------------
-code.open_in_new_tab=Open in New Tab
+code.open_component_page=Open Component's Page