]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7228 Create new "My Issues" page 5.4-M10
authorStas Vilchik <vilchiks@gmail.com>
Thu, 28 Jan 2016 16:10:59 +0000 (17:10 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Thu, 28 Jan 2016 16:10:59 +0000 (17:10 +0100)
it/it-tests/src/test/java/it/user/MyAccountPageTest.java
it/it-tests/src/test/resources/user/MyAccountPageTest/should_display_issues.html [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/app.js
server/sonar-web/src/main/js/apps/account/components/Nav.js
server/sonar-web/src/main/js/apps/account/containers/AccountApp.js
server/sonar-web/src/main/js/apps/account/containers/IssuesContainer.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/issues-app.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/styles/account.css
server/sonar-web/src/main/js/apps/issues/workspace-list-view.js
server/sonar-web/src/main/js/components/navigator/workspace-list-view.js

index 5188171ec238ef59ae97275cdb321933748ed2f7..fb4704beba4af40d1412a4b704c83ddcbfc2c7e5 100644 (file)
@@ -66,6 +66,14 @@ public class MyAccountPageTest {
     new SeleneseTest(selenese).runOn(orchestrator);
   }
 
+  @Test
+  public void should_display_issues() throws Exception {
+    Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("should_display_issues",
+      "/user/MyAccountPageTest/should_display_issues.html"
+    ).build();
+    new SeleneseTest(selenese).runOn(orchestrator);
+  }
+
   private static void createUser(String login, String name, String email) {
     adminWsClient.wsConnector().call(
       new PostRequest("api/users/create")
diff --git a/it/it-tests/src/test/resources/user/MyAccountPageTest/should_display_issues.html b/it/it-tests/src/test/resources/user/MyAccountPageTest/should_display_issues.html
new file mode 100644 (file)
index 0000000..090cb4f
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <link rel="selenium.base" href="http://localhost:49506"/>
+  <title>should_display_issues</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <thead>
+  <tr>
+    <td rowspan="1" colspan="3">should_display_issues</td>
+  </tr>
+  </thead>
+  <tbody>
+  <tr>
+       <td>open</td>
+       <td>/sonar/sessions/login</td>
+       <td></td>
+</tr>
+<tr>
+       <td>type</td>
+       <td>login</td>
+       <td>account-user</td>
+</tr>
+<tr>
+       <td>type</td>
+       <td>password</td>
+       <td>password</td>
+</tr>
+<tr>
+       <td>clickAndWait</td>
+       <td>commit</td>
+       <td></td>
+</tr>
+<tr>
+       <td>open</td>
+       <td>/sonar/account/issues</td>
+       <td></td>
+</tr>
+<tr>
+       <td>waitForElementPresent</td>
+       <td>css=[data-unresolved]</td>
+       <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
index dc52a4fc013f50b11c87fddb45432e97f4b8bd77..8004bfda5862fd1e7196f3f2dcff256273e1c6c3 100644 (file)
@@ -28,6 +28,7 @@ import AccountApp from './containers/AccountApp';
 import Home from './components/Home';
 import NotificationsContainer from './containers/NotificationsContainer';
 import SecurityContainer from './containers/SecurityContainer';
+import IssuesContainer from './containers/IssuesContainer';
 
 import './styles/account.css';
 
@@ -45,6 +46,7 @@ window.sonarqube.appStarted.then(options => {
         <Router history={history}>
           <Route path="/" component={AccountApp}>
             <IndexRoute component={Home}/>
+            <Route path="issues" component={IssuesContainer}/>
             <Route path="notifications" component={NotificationsContainer}/>
             <Route path="security" component={SecurityContainer}/>
 
index fe826c181845b7a1d62beb78a1b8f66f551135c8..a1fe74bfc0dcb89236ee309268ed59c19c2fb23c 100644 (file)
@@ -34,6 +34,13 @@ const Nav = ({ user }) => (
               <i className="icon-home"/>
             </IndexLink>
           </li>
+          <li>
+            <a
+                className={window.location.pathname === `${window.baseUrl}/account/issues` && 'active'}
+                href={`${window.baseUrl}/account/issues`}>
+              {translate('issues.page')}
+            </a>
+          </li>
           <li>
             <IndexLink to="notifications" activeClassName="active">
               {translate('my_account.notifications')}
index f9b06e63044a38ba8b8ed2e23f0cafaccc8cb61f..9559d9336408ddedce9ccc00957d9db85cc079c0 100644 (file)
@@ -48,7 +48,7 @@ export default class AccountApp extends Component {
     });
 
     return (
-        <div>
+        <div className="account-page">
           <Nav user={user}/>
           {children}
         </div>
diff --git a/server/sonar-web/src/main/js/apps/account/containers/IssuesContainer.js b/server/sonar-web/src/main/js/apps/account/containers/IssuesContainer.js
new file mode 100644 (file)
index 0000000..67e927f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * 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, { Component } from 'react';
+
+import IssuesApp from '../issues-app';
+
+export default class IssuesContainer extends Component {
+  componentDidMount () {
+    this.issuesApp = IssuesApp;
+    this.issuesApp.start({
+      el: this.refs.container
+    });
+  }
+
+  componentWillUnmount () {
+    this.issuesApp.stop();
+  }
+
+  render () {
+    return <div ref="container"></div>;
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/account/issues-app.js b/server/sonar-web/src/main/js/apps/account/issues-app.js
new file mode 100644 (file)
index 0000000..25c83b1
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * SonarQube
+ * 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 $ from 'jquery';
+import _ from 'underscore';
+import Backbone from 'backbone';
+import Marionette from 'backbone.marionette';
+import State from '../issues/models/state';
+import Layout from '../issues/layout';
+import Issues from '../issues/models/issues';
+import Facets from '../../components/navigator/models/facets';
+import Filters from '../issues/models/filters';
+import Controller from '../issues/controller';
+import Router from '../issues/router';
+import WorkspaceListView from '../issues/workspace-list-view';
+import WorkspaceHeaderView from '../issues/workspace-header-view';
+import FacetsView from './../issues/facets-view';
+
+var App = new Marionette.Application(),
+    init = function (options) {
+      this.config = options.config;
+      this.state = new State({
+        isContext: true,
+        contextQuery: { assignees: '__me__' }
+      });
+      this.updateContextFacets();
+      this.list = new Issues();
+      this.facets = new Facets();
+      this.filters = new Filters();
+
+      this.layout = new Layout({ app: this });
+      this.layout.$el.appendTo(options.el);
+      this.layout.render();
+      $('#footer').addClass('search-navigator-footer');
+
+      this.controller = new Controller({ app: this });
+
+      this.issuesView = new WorkspaceListView({
+        app: this,
+        collection: this.list
+      });
+      this.layout.workspaceListRegion.show(this.issuesView);
+      this.issuesView.bindScrollEvents();
+
+      this.workspaceHeaderView = new WorkspaceHeaderView({
+        app: this,
+        collection: this.list
+      });
+      this.layout.workspaceHeaderRegion.show(this.workspaceHeaderView);
+
+      this.facetsView = new FacetsView({
+        app: this,
+        collection: this.facets
+      });
+      this.layout.facetsRegion.show(this.facetsView);
+
+      this.controller.fetchFilters().done(function () {
+        key.setScope('list');
+        App.router = new Router({ app: App });
+        Backbone.history.start();
+      });
+    };
+
+App.getContextQuery = function () {
+  return { assignees: '__me__' };
+};
+
+App.updateContextFacets = function () {
+  var facets = this.state.get('facets'),
+      allFacets = this.state.get('allFacets'),
+      facetsFromServer = this.state.get('facetsFromServer');
+  return this.state.set({
+    facets: facets,
+    allFacets: _.difference(allFacets, ['assignees']),
+    facetsFromServer: _.difference(facetsFromServer, ['assignees'])
+  });
+};
+
+App.stop = function () {
+  App.layout.destroy();
+  Backbone.history.stop();
+};
+
+App.on('start', function (options) {
+  init.call(App, options);
+});
+
+export default App;
index e308d9234bc74954fb41b59a5094fc32cf8c1424..4105365b0cbb2d3c47e8f6d323d7cb0dc77ee32e 100644 (file)
@@ -1,4 +1,9 @@
 .account-header {
+  position: fixed;
+  top: 30px;
+  left: 0;
+  right: 0;
+  z-index: 420;
   background-color: #f3f3f3;
 }
 
@@ -18,3 +23,7 @@
   float: left;
   margin-right: 20px;
 }
+
+.account-page {
+  padding-top: 102px;
+}
index 5fb6d6aa92c0b453a86d6f2d3127c3af50e451a0..6e471a45d122cf109631999054b6b863d09a8017 100644 (file)
@@ -73,11 +73,24 @@ export default WorkspaceListView.extend({
     key('c', 'list', function () {
       return doAction('comment');
     });
-    return key('t', 'list', function () {
+    key('t', 'list', function () {
       return doAction('edit-tags');
     });
   },
 
+  unbindShortcuts: function () {
+    WorkspaceListView.prototype.unbindShortcuts.apply(this, arguments);
+    key.unbind('right', 'list');
+    key.unbind('space', 'list');
+    key.unbind('f', 'list');
+    key.unbind('a', 'list');
+    key.unbind('m', 'list');
+    key.unbind('p', 'list');
+    key.unbind('i', 'list');
+    key.unbind('c', 'list');
+    key.unbind('t', 'list');
+  },
+
   scrollTo: function () {
     var selectedIssue = this.collection.at(this.options.app.state.get('selectedIndex'));
     if (selectedIssue == null) {
index 6017b69ff3b0b0271da97fe141edb59d4b8276a5..5069792da09e3aba8d2bab59713d60d08d4cf6bc 100644 (file)
@@ -86,6 +86,11 @@ export default Marionette.CompositeView.extend({
     });
   },
 
+  unbindShortcuts: function () {
+    key.unbind('up', 'list');
+    key.unbind('down', 'list');
+  },
+
   loadMore: function () {
     if (!this.options.app.state.get('maxResultsReached')) {
       var that = this;