From d10d21c9f178d5ccad5884695a2975c970c875c8 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Fri, 11 Sep 2015 14:40:32 +0200 Subject: [PATCH] SONAR-6285 show filters on the issues home page --- .../src/main/js/apps/issues/controller.js | 7 +- .../src/main/js/apps/issues/filters-view.js | 2 +- .../apps/issues/templates/issues-filters.hbs | 28 ++- .../templates/issues-workspace-home.hbs | 93 +++------- .../js/apps/issues/workspace-home-view.js | 159 +----------------- .../common/handlebars-extensions.js | 5 + .../sonar-web/src/main/less/pages/issues.less | 43 ++--- .../test/json/issues-spec/issue-filters.json | 6 +- server/sonar-web/test/medium/issues.spec.js | 27 +-- 9 files changed, 71 insertions(+), 299 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/issues/controller.js b/server/sonar-web/src/main/js/apps/issues/controller.js index a35311fa576..a201496f8b8 100644 --- a/server/sonar-web/src/main/js/apps/issues/controller.js +++ b/server/sonar-web/src/main/js/apps/issues/controller.js @@ -90,7 +90,7 @@ define([ fetchFilters: function () { var that = this; return $.when( - that.options.app.filters.fetch(), + that.options.app.filters.fetch({ reset: true }), $.get(baseUrl + '/api/issue_filters/app', function (r) { that.options.app.state.set({ canBulkChange: r.canBulkChange, @@ -237,7 +237,10 @@ define([ this.options.app.layout.workspaceComponentViewerRegion.reset(); key.setScope('home'); this.options.app.issuesView.unbindScrollEvents(); - this.options.app.homeView = new HomeView({ app: this.options.app }); + this.options.app.homeView = new HomeView({ + app: this.options.app, + collection: this.options.app.filters + }); this.options.app.layout.workspaceHomeRegion.show(this.options.app.homeView); return this.options.app.layout.showHomePage(); }, diff --git a/server/sonar-web/src/main/js/apps/issues/filters-view.js b/server/sonar-web/src/main/js/apps/issues/filters-view.js index d219c1e0fd4..c3ea67bd434 100644 --- a/server/sonar-web/src/main/js/apps/issues/filters-view.js +++ b/server/sonar-web/src/main/js/apps/issues/filters-view.js @@ -20,7 +20,7 @@ define([ var that = this; this.listenTo(options.app.state, 'change:filter', this.render); this.listenTo(options.app.state, 'change:changed', this.render); - this.listenTo(options.app.filters, 'all', this.render); + this.listenTo(options.app.filters, 'reset', this.render); window.onSaveAs = window.onCopy = window.onEdit = function (id) { $('#modal').dialog('close'); return that.options.app.controller.fetchFilters().done(function () { diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-filters.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-filters.hbs index c3ffd012a3a..74cd306ce3e 100644 --- a/server/sonar-web/src/main/js/apps/issues/templates/issues-filters.hbs +++ b/server/sonar-web/src/main/js/apps/issues/templates/issues-filters.hbs @@ -1,29 +1,27 @@ {{#unless state.isContext}}

- {{#if state.canManageFilters}} - - {{> "_issues-filter-name"}} - - + {{#if filter.description}} +
{{filter.description}}
{{/if}}

diff --git a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-home.hbs b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-home.hbs index 6c903b7faf9..0057630ed6a 100644 --- a/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-home.hbs +++ b/server/sonar-web/src/main/js/apps/issues/templates/issues-workspace-home.hbs @@ -1,77 +1,26 @@ -
-
-
-

{{t "issues.home.recent_issues"}}

-

({{t "issues.home.over_last_week"}})

+
+
+

Start From Filters:

-
-

{{t "issues.home.projects"}}

- - {{#each projects}} - - - - - {{/each}} -
{{qualifierIcon "TRK"}} {{label}}+{{numberShort count}}
-

{{t "issues.home.authors"}}

- - {{#each authors}} - - - - - {{/each}} -
{{val}}+{{numberShort count}}
-

{{t "issues.home.tags"}}

-
    - {{#each tags}} -
  • {{val}}
  • - {{/each}} -
-
+
    + {{#each items}} + {{#ifCanUseFilter query}} +
  • + - {{#if user}} -
    -

    {{t "issues.home.my_recent_issues"}}

    -

    ({{t "issues.home.over_last_week"}})

    + {{name}} -
    - {{#notEmpty myProjects}} -

    {{t "issues.home.projects"}}

    - - {{#each myProjects}} - - - - - {{/each}} -
    {{qualifierIcon "TRK"}} {{label}}+{{numberShort count}}
    - {{/notEmpty}} - {{#notEmpty myTags}} -

    {{t "issues.home.tags"}}

    -
      - {{#each myTags}} -
    • {{val}}
    • - {{/each}} -
    - {{/notEmpty}} - {{#notEmpty filters}} -

    - {{t "issues.home.my_filters"}}

    -
      - {{#each filters}} -
    • - {{name}} -
    • - {{/each}} -
    - {{/notEmpty}} -
    - {{/if}} + {{#if shared}} + [{{t "issue_filter.shared"}}] + {{/if}} +
  • + {{/ifCanUseFilter}} + {{/each}} +
+
+ +
+ or make a + New Search
diff --git a/server/sonar-web/src/main/js/apps/issues/workspace-home-view.js b/server/sonar-web/src/main/js/apps/issues/workspace-home-view.js index 942e86e4ba7..aff1c52366f 100644 --- a/server/sonar-web/src/main/js/apps/issues/workspace-home-view.js +++ b/server/sonar-web/src/main/js/apps/issues/workspace-home-view.js @@ -2,169 +2,12 @@ define([ './templates' ], function () { - var $ = jQuery; - - Handlebars.registerHelper('issuesHomeLink', function (property, value) { - return baseUrl + '/issues/search#resolved=false|createdInLast=1w|' + - property + '=' + (encodeURIComponent(value)); - }); - - Handlebars.registerHelper('myIssuesHomeLink', function (property, value) { - return baseUrl + '/issues/search#resolved=false|createdInLast=1w|assignees=__me__|' + - property + '=' + (encodeURIComponent(value)); - }); - Handlebars.registerHelper('issueFilterHomeLink', function (id) { return baseUrl + '/issues/search#id=' + id; }); return Marionette.ItemView.extend({ - template: Templates['issues-workspace-home'], - - modelEvents: { - 'change': 'render' - }, - - events: { - 'click .js-barchart rect': 'selectBar', - 'click .js-my-barchart rect': 'selectMyBar' - }, - - initialize: function () { - this.model = new Backbone.Model(); - this.requestIssues(); - this.requestMyIssues(); - }, - - _getProjects: function (r) { - var projectFacet = _.findWhere(r.facets, { property: 'projectUuids' }); - if (projectFacet != null) { - var values = _.head(projectFacet.values, 3); - values.forEach(function (v) { - var project = _.findWhere(r.components, { uuid: v.val }); - v.label = project.longName; - }); - return values; - } - }, - - _getAuthors: function (r) { - var authorFacet = _.findWhere(r.facets, { property: 'authors' }); - if (authorFacet != null) { - return _.head(authorFacet.values, 3); - } - }, - - _getTags: function (r) { - var MIN_SIZE = 10, - MAX_SIZE = 24, - tagFacet = _.findWhere(r.facets, { property: 'tags' }); - if (tagFacet != null) { - var values = _.head(tagFacet.values, 10), - minCount = _.min(values, function (v) { - return v.count; - }).count, - maxCount = _.max(values, function (v) { - return v.count; - }).count, - scale = d3.scale.linear().domain([minCount, maxCount]).range([MIN_SIZE, MAX_SIZE]); - values.forEach(function (v) { - v.size = scale(v.count); - }); - return values; - } - }, - - requestIssues: function () { - var that = this; - var url = baseUrl + '/api/issues/search', - options = { - resolved: false, - createdInLast: '1w', - ps: 1, - facets: 'createdAt,projectUuids,authors,tags' - }; - return $.get(url, options).done(function (r) { - var createdAt = _.findWhere(r.facets, { property: 'createdAt' }); - that.model.set({ - createdAt: createdAt != null ? createdAt.values : null, - projects: that._getProjects(r), - authors: that._getAuthors(r), - tags: that._getTags(r) - }); - }); - }, - - requestMyIssues: function () { - var that = this; - var url = baseUrl + '/api/issues/search', - options = { - resolved: false, - createdInLast: '1w', - assignees: '__me__', - ps: 1, - facets: 'createdAt,projectUuids,authors,tags' - }; - return $.get(url, options).done(function (r) { - var createdAt = _.findWhere(r.facets, { property: 'createdAt' }); - return that.model.set({ - myCreatedAt: createdAt != null ? createdAt.values : null, - myProjects: that._getProjects(r), - myTags: that._getTags(r) - }); - }); - }, - - _addFormattedValues: function (values) { - return values.map(function (v) { - var text = window.formatMeasure(v.count, 'SHORT_INT'); - return _.extend(v, { text: text }); - }); - }, - - onRender: function () { - var values = this.model.get('createdAt'), - myValues = this.model.get('myCreatedAt'); - if (values != null) { - this.$('.js-barchart').barchart(this._addFormattedValues(values)); - } - if (myValues != null) { - this.$('.js-my-barchart').barchart(this._addFormattedValues(myValues)); - } - this.$('[data-toggle="tooltip"]').tooltip({ container: 'body' }); - }, - - onDestroy: function () { - this.$('[data-toggle="tooltip"]').tooltip('destroy'); - }, - - selectBar: function (e) { - var periodStart = $(e.currentTarget).data('period-start'), - periodEnd = $(e.currentTarget).data('period-end'); - this.options.app.state.setQuery({ - resolved: false, - createdAfter: periodStart, - createdBefore: periodEnd - }); - }, - - selectMyBar: function (e) { - var periodStart = $(e.currentTarget).data('period-start'), - periodEnd = $(e.currentTarget).data('period-end'); - this.options.app.state.setQuery({ - resolved: false, - assignees: '__me__', - createdAfter: periodStart, - createdBefore: periodEnd - }); - }, - - serializeData: function () { - return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), { - user: window.SS.user, - filters: _.sortBy(this.options.app.filters.toJSON(), 'name') - }); - } + template: Templates['issues-workspace-home'] }); }); diff --git a/server/sonar-web/src/main/js/components/common/handlebars-extensions.js b/server/sonar-web/src/main/js/components/common/handlebars-extensions.js index 557e883f087..fd1a7ddb74f 100644 --- a/server/sonar-web/src/main/js/components/common/handlebars-extensions.js +++ b/server/sonar-web/src/main/js/components/common/handlebars-extensions.js @@ -581,4 +581,9 @@ ); }); + Handlebars.registerHelper('ifCanUseFilter', function (query, options) { + var cond = window.SS.user || query.indexOf('__me__') === -1; + return cond ? options.fn(this) : options.inverse(this); + }); + })(); diff --git a/server/sonar-web/src/main/less/pages/issues.less b/server/sonar-web/src/main/less/pages/issues.less index cfa2cc22bba..6345b473eba 100644 --- a/server/sonar-web/src/main/less/pages/issues.less +++ b/server/sonar-web/src/main/less/pages/issues.less @@ -1,22 +1,3 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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 (reference) "../variables"; @import (reference) "../mixins"; @import (reference) "../components/ui"; @@ -29,8 +10,7 @@ &.sticky { .issues-workspace-list, - .issues-workspace-component-viewer, - .issues-workspace-home { + .issues-workspace-component-viewer { padding-top: 22px + 5px + 5px + 1px + 10px; } @@ -161,6 +141,11 @@ display: none; } + .issues-header-component, + .search-navigator-header-actions { + visibility: hidden; + } + .issues-workspace-home { display: block; } @@ -168,14 +153,14 @@ .issues-workspace-home { display: none; - width: ~"calc(100vw - @{sideWidth})"; - max-width: 900px; - margin-left: auto; - margin-right: auto; - padding-left: 10px; - padding-right: 10px; - .box-sizing(border-box); - overflow: hidden; + padding-top: 180px; + text-align: center; +} + +.issues-workspace-home-inner { + display: inline-block; + vertical-align: top; + max-width: 600px; } .issues-facet-mode { diff --git a/server/sonar-web/src/test/json/issues-spec/issue-filters.json b/server/sonar-web/src/test/json/issues-spec/issue-filters.json index a4eb6979edd..290451d7485 100644 --- a/server/sonar-web/src/test/json/issues-spec/issue-filters.json +++ b/server/sonar-web/src/test/json/issues-spec/issue-filters.json @@ -7,7 +7,7 @@ "shared": true, "query": "resolved=false", "canModify": true, - "favourite": false + "favorite": false }, { "id": "2", @@ -16,7 +16,7 @@ "shared": true, "query": "resolutions=FALSE-POSITIVE,WONTFIX", "canModify": true, - "favourite": false + "favorite": false }, { "id": "3", @@ -25,7 +25,7 @@ "shared": true, "query": "resolved=false|assignees=__me__", "canModify": true, - "favourite": false + "favorite": false } ] } diff --git a/server/sonar-web/test/medium/issues.spec.js b/server/sonar-web/test/medium/issues.spec.js index 809f82b7593..767ddee9fc2 100644 --- a/server/sonar-web/test/medium/issues.spec.js +++ b/server/sonar-web/test/medium/issues.spec.js @@ -11,12 +11,9 @@ define(function (require) { .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') .mockFromFile('/api/issues/search', 'issues-spec/search.json') .startApp('issues') - .sleep(2000) - .clickElement('.js-new-search') - .checkElementCount('.js-filter', 3) + .checkElementCount('.js-filter', 2) .checkElementCount('.js-filter[data-id="1"]', 1) - .checkElementCount('.js-filter[data-id="2"]', 1) - .checkElementCount('.js-filter[data-id="3"]', 1); + .checkElementCount('.js-filter[data-id="2"]', 1); }); bdd.it('should load a saved search', function () { @@ -27,7 +24,6 @@ define(function (require) { .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') .mockFromFile('/api/issues/search', 'issues-spec/search.json') .startApp('issues') - .clickElement('.js-new-search') .clickElement('.search-navigator-filters-show-list') .clickElement('.js-filter[data-id="2"]') .checkElementCount('.js-filter-copy', 1) @@ -44,7 +40,6 @@ define(function (require) { .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') .mockFromFile('/api/issues/search', 'issues-spec/search.json') .startApp('issues') - .clickElement('.js-new-search') .clickElement('.search-navigator-filters-show-list') .clickElement('.js-filter[data-id="2"]') .checkElementCount('.js-filter-copy', 1) @@ -59,13 +54,12 @@ define(function (require) { bdd.it('should load', function () { return this.remote - .open() + .open('#resolved=false') .mockFromString('/api/l10n/index', '{}') .mockFromFile('/api/issue_filters/app', 'issues-spec/app.json') .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') .mockFromFile('/api/issues/search', 'issues-spec/search.json') .startApp('issues') - .clickElement('.js-new-search') .checkElementExist('.facet[data-value=BLOCKER]') .checkElementExist('.facet[data-value=CRITICAL]') .checkElementExist('.facet[data-value=MAJOR]') @@ -99,13 +93,12 @@ define(function (require) { bdd.it('should show severity facet', function () { return this.remote - .open() + .open('#resolved=false') .mockFromString('/api/l10n/index', '{}') .mockFromFile('/api/issue_filters/app', 'issues-spec/app.json') .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') .mockFromFile('/api/issues/search', 'issues-spec/search.json') .startApp('issues') - .clickElement('.js-new-search') .checkElementCount('.issue', 50) .clearMocks() .mockFromFile('/api/issues/search', 'issues-spec/search-reopened.json', { data: { severities: 'BLOCKER' } }) @@ -118,13 +111,12 @@ define(function (require) { issueSelector = '.issue[data-key="' + issueKey + '"]'; return this.remote - .open() + .open('#resolved=false') .mockFromString('/api/l10n/index', '{}') .mockFromFile('/api/issue_filters/app', 'issues-spec/app.json') .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') .mockFromFile('/api/issues/search', 'issues-spec/search.json') .startApp('issues') - .clickElement('.js-new-search') .checkElementExist('.js-selection') .checkElementNotExist('.js-selection.icon-checkbox-checked') .checkElementExist('.issue .js-toggle') @@ -143,7 +135,7 @@ define(function (require) { bdd.it('should bulk change issues', function () { return this.remote - .open() + .open('#resolved=false') .mockFromString('/api/l10n/index', '{}') .mockFromFile('/api/issue_filters/app', 'issues-spec/app.json') .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') @@ -151,7 +143,6 @@ define(function (require) { .mockFromString('/issues/bulk_change_form*', '
bulk change form
', { contentType: 'text/plain' }) .startApp('issues') - .clickElement('.js-new-search') .clickElement('#issues-bulk-change') .clickElement('.js-bulk-change') .checkElementExist('#bulk-change-form') @@ -163,7 +154,7 @@ define(function (require) { issueSelector = '.issue[data-key="' + issueKey + '"]'; return this.remote - .open() + .open('#resolved=false') .mockFromString('/api/l10n/index', '{}') .mockFromFile('/api/issue_filters/app', 'issues-spec/app.json') .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') @@ -171,7 +162,6 @@ define(function (require) { .mockFromString('/issues/bulk_change_form*', '
bulk change form
', { contentType: 'text/plain' }) .startApp('issues') - .clickElement('.js-new-search') .checkElementExist('.js-selection') .checkElementNotExist('.js-selection.icon-checkbox-checked') .checkElementExist('.issue .js-toggle') @@ -194,13 +184,12 @@ define(function (require) { bdd.it('should filter similar issues', function () { return this.remote - .open() + .open('#resolved=false') .mockFromString('/api/l10n/index', '{}') .mockFromFile('/api/issue_filters/app', 'issues-spec/app.json') .mockFromFile('/api/issue_filters/search', 'issues-spec/issue-filters.json') .mockFromFile('/api/issues/search', 'issues-spec/search-filter-similar-issues.json') .startApp('issues') - .clickElement('.js-new-search') .checkElementCount('.issue', 2) .clickElement('.issue.selected .js-issue-filter') .checkElementExist('.bubble-popup') -- 2.39.5