]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5855 Update UI of quality gates page
authorStas Vilchik <vilchiks@gmail.com>
Wed, 18 Feb 2015 14:08:54 +0000 (15:08 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 18 Feb 2015 14:16:42 +0000 (15:16 +0100)
17 files changed:
server/sonar-web/src/main/coffee/quality-gate/app.coffee
server/sonar-web/src/main/coffee/quality-gate/layout.coffee
server/sonar-web/src/main/coffee/quality-gate/router.coffee
server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee
server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee
server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-condition.hbs
server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-header.hbs
server/sonar-web/src/main/hbs/quality-gates/quality-gate-sidebar-list-item.hbs
server/sonar-web/src/main/hbs/quality-gates/quality-gates-layout.hbs
server/sonar-web/src/main/js/tests/e2e/tests/quality-gates-spec.js
server/sonar-web/src/main/js/tests/e2e/views/quality-gates.jade
server/sonar-web/src/main/less/components/ui.less
server/sonar-web/src/main/less/init.less
server/sonar-web/src/main/less/init/forms.less [new file with mode: 0644]
server/sonar-web/src/main/less/pages/quality-gates.less
server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb

index 28cf9daf416f7a281b6773c9137bee102dd6a493..1e376e3b2b5b4d1d161af92533e64eb3a6d4023c 100644 (file)
@@ -18,10 +18,6 @@ requirejs [
   QualityGateLayout
 ) ->
 
-  # Add html class to mark the page as navigator page
-  jQuery('html').addClass('navigator-page quality-gates-page');
-
-
   # Create a Quality Gate Application
   App = new Marionette.Application
 
@@ -49,8 +45,8 @@ requirejs [
   # Construct layout
   App.addInitializer ->
     @layout = new QualityGateLayout app: @
-    jQuery('#content').append @layout.render().el
-    @layout.onResize()
+    jQuery('#quality-gates').append @layout.render().el
+    jQuery('#footer').addClass 'search-navigator-footer'
 
 
   # Construct actions bar
@@ -101,8 +97,5 @@ requirejs [
 
   jQuery.when(qualityGatesXHR, appXHR, l10nXHR)
     .done ->
-      # Remove the initial spinner
-      jQuery('#quality-gate-page-loader').remove()
-
       # Start the application
       App.start()
index 9a982cf1e02ae665eec59b3edbbaa897cc0bbbc6..4e58fa172e8084ec580527b64bb6b5156b9c7d46 100644 (file)
@@ -2,45 +2,21 @@ define [
   'templates/quality-gates'
 ], ->
 
+  $ = jQuery
+
   class AppLayout extends Marionette.Layout
-    className: 'navigator quality-gates-navigator'
     template: Templates['quality-gates-layout']
 
 
     regions:
-      headerRegion: '.navigator-header'
-      actionsRegion: '.navigator-actions'
-      resultsRegion: '.navigator-results'
-      detailsRegion: '.navigator-details'
-
-
-    initialize: (options) ->
-      @listenTo options.app.qualityGates, 'all', @updateLayout
-      jQuery(window).on 'resize', => @onResize()
-
-
-    updateLayout: ->
-      empty = @options.app.qualityGates.length == 0
-      @$(@headerRegion.el).toggle !empty
-      @$(@detailsRegion.el).toggle !empty
-
-
-    onResize: ->
-      footerEl = jQuery('#footer')
-      footerHeight = footerEl.outerHeight true
-
-      resultsEl = jQuery('.navigator-results')
-      resultsHeight = jQuery(window).height() - resultsEl.offset().top -
-      parseInt(resultsEl.css('margin-bottom'), 10) - footerHeight
-      resultsEl.height resultsHeight
-
-      detailsEl = jQuery('.navigator-details')
-      detailsWidth = jQuery(window).width() - detailsEl.offset().left -
-      parseInt(detailsEl.css('margin-right'), 10)
-      detailsHeight = jQuery(window).height() - detailsEl.offset().top -
-      parseInt(detailsEl.css('margin-bottom'), 10) - footerHeight
-      detailsEl.width(detailsWidth).height detailsHeight
+      headerRegion: '.search-navigator-workspace-header'
+      actionsRegion: '.search-navigator-filters'
+      resultsRegion: '.quality-gates-results'
+      detailsRegion: '.search-navigator-workspace-list'
 
 
     onRender: ->
-      @updateLayout()
+      $('.search-navigator').addClass 'sticky'
+      top = $('.search-navigator').offset().top
+      @$('.search-navigator-workspace-header').css top: top
+      @$('.search-navigator-side').css({ top: top }).isolatedScroll()
index 9bb56410f9aebbc66357711408b9030d4bcfdfcd..e185428a51b994df58a9cfe4898b17a8baa9e1cc 100644 (file)
@@ -34,7 +34,5 @@ define [
         @app.layout.detailsRegion.show qualityGateDetailView
         qualityGateDetailView.$el.hide()
 
-        qualityGateDetailHeaderView.showSpinner()
         qualityGate.fetch().done ->
           qualityGateDetailView.$el.show()
-          qualityGateDetailHeaderView.hideSpinner()
index 3bcc07e9eafce7d20bf0c9e307d34add734712a2..a90b5580f2aa5116bcfafc69f5f269550af864cf 100644 (file)
@@ -49,26 +49,21 @@ define [
         yesLabel: t 'delete'
         noLabel: t 'cancel'
         yesHandler: =>
-          @showSpinner()
           jQuery.ajax
             type: 'POST'
             url: "#{baseUrl}/api/qualitygates/destroy"
             data: id: @model.id
-          .always => @hideSpinner()
           .done => @options.app.deleteQualityGate @model.id
         always: => @ui.deleteButton.blur()
 
 
     changeDefault: (set) ->
-      @showSpinner()
       data = if set then { id: @model.id } else {}
       method = if set then 'set_as_default' else 'unset_default'
       jQuery.ajax
         type: 'POST'
         url: "#{baseUrl}/api/qualitygates/#{method}"
         data: data
-      .always =>
-        @hideSpinner()
       .done =>
         @options.app.unsetDefaults @model.id
         @model.set 'default', !@model.get('default')
@@ -82,15 +77,5 @@ define [
       @changeDefault false
 
 
-    showSpinner: ->
-      @$el.hide()
-      jQuery(@spinner).insertBefore @$el
-
-
-    hideSpinner: ->
-      @$el.prev().remove()
-      @$el.show()
-
-
     serializeData: ->
       _.extend super, canEdit: @options.app.canEdit
index d2c618fa745faa0d66fc036da9f13540ffd218ba..4f2e3eaa14c4a68908f437a6cd56d8343b8db087 100644 (file)
@@ -3,7 +3,7 @@ define [
 ], ->
 
   class QualityGateSidebarListItemView extends Marionette.ItemView
-    tagName: 'li'
+    className: 'facet search-navigator-facet'
     template: Templates['quality-gate-sidebar-list-item']
 
 
index 063fd569a2c8a74072d3df9ff9474d06edb7aee1..7cdfbe4a14e2b7c490075bf7edbe90fcb7bf3b47 100644 (file)
@@ -7,8 +7,7 @@ define [
 ) ->
 
   class QualityGateSidebarListView extends Marionette.CollectionView
-    tagName: 'ol'
-    className: 'navigator-results-list'
+    className: 'search-navigator-facet-list'
     itemView: QualityGateSidebarListItemView
     emptyView: QualityGateSidebarListEmptyView
 
index 79973fe9a3808578007057f8f68e25dda7cf1d9d..9be26807f78329ba1f705c3a5605f7abea07a94f 100644 (file)
     {{t 'quality_gates.operator' op}}
   {{/if}}
 </td>
-<td width="15%" nowrap="nowrap">
+<td width="1" class="nowrap">
   <i class="icon-alert-warn" title="{{t 'quality_gates.warning_tooltip'}}"></i>
   {{#if canEdit}}
-    <input name="warning" class="measure-input" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
+    <input name="warning" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
            type="text">
   {{else}}
     {{warning}}
   {{/if}}
 </td>
-<td width="15%" nowrap="nowrap">
+<td width="1" class="nowrap">
   <i class="icon-alert-error" title="{{t 'quality_gates.error_tooltip'}}"></i>
   {{#if canEdit}}
-    <input name="error" class="measure-input" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
+    <input name="error" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
            type="text">
   {{else}}
     {{error}}
index e84a730d379e9db6ab9b22ac287b2a6dcb3e30f0..71f7e3b06de0bfefcb33daba99082207dd664aac 100644 (file)
@@ -1,14 +1,16 @@
-<h1 class="navigator-header-title">{{name}}</h1>
+<h2 class="search-navigator-header-component">{{name}}</h2>
 
 {{#if canEdit}}
-  <div class="navigator-header-actions button-group">
-    <button id="quality-gate-rename">{{t 'rename'}}</button>
-    <button id="quality-gate-copy">{{t 'copy'}}</button>
-    {{#if default}}
-      <button id="quality-gate-unset-as-default">{{t 'unset_as_default'}}</button>
-    {{else}}
-      <button id="quality-gate-set-as-default">{{t 'set_as_default'}}</button>
-    {{/if}}
-    <button id="quality-gate-delete" class="button-red">{{t 'delete'}}</button>
+  <div class="search-navigator-header-actions">
+    <div class="button-group">
+      <button id="quality-gate-rename">{{t 'rename'}}</button>
+      <button id="quality-gate-copy">{{t 'copy'}}</button>
+      {{#if default}}
+        <button id="quality-gate-unset-as-default">{{t 'unset_as_default'}}</button>
+      {{else}}
+        <button id="quality-gate-set-as-default">{{t 'set_as_default'}}</button>
+      {{/if}}
+      <button id="quality-gate-delete" class="button-red">{{t 'delete'}}</button>
+    </div>
   </div>
-{{/if}}
\ No newline at end of file
+{{/if}}
index af8d93ae07b0b56e3c61ebeb244f145382328333..2b8f1089eb76311207a0836fc5ba3c16f4d7a843 100644 (file)
@@ -1 +1,7 @@
-<div class="line line-nowrap">{{name}} {{#if default}}<span class="subtitle">({{t 'default'}})</span>{{/if}}</div>
\ No newline at end of file
+<span class="facet-name">
+  {{name}}
+</span>
+
+{{#if default}}
+  <span class="facet-stat">{{t 'default'}}</span>
+{{/if}}
index 22f6fba05e7b8d6376385ce030d6622eb409d01b..d596459b6a704fde1a5032c8cfbd1df8334550d0 100644 (file)
@@ -1,10 +1,9 @@
-<div class="navigator-content">
-  <div class="navigator-side">
-    <div class="navigator-actions"></div>
-    <div class="navigator-results quality-gates-nav"></div>
-  </div>
-  <div class="navigator-main">
-    <div class="navigator-header"></div>
-    <div class="navigator-details"></div>
-  </div>
-</div>
\ No newline at end of file
+<div class="search-navigator-side quality-gates-side">
+  <div class="search-navigator-filters"></div>
+  <div class="quality-gates-results"></div>
+</div>
+
+<div class="search-navigator-workspace">
+  <div class="search-navigator-workspace-header"></div>
+  <div class="search-navigator-workspace-list"></div>
+</div>
index f068858dd676d8cbe2f9ebe3788c2f209ff05c73..6655b4eb9ac4da1747a3b84af3ed9e3772b9f8b6 100644 (file)
@@ -1,36 +1,42 @@
+/* global casper:false */
+
 var lib = require('../lib');
 
 lib.initMessages();
 lib.changeWorkingDirectory('quality-gates-spec');
 
 
-casper.test.begin('Quality Gates', function suite(test) {
-  casper.start(lib.buildUrl('quality-gates'), function() {
-    lib.setDefaultViewport();
-
-    lib.mockRequest('/api/l10n/index', '{}');
-    lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
-    lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
-    lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
-  });
-
-  casper.waitWhileSelector("div#quality-gates-loader", function() {
-
-    casper.waitForSelector('li.active', function() {
-      test.assertElementCount('li.active', 1);
-      test.assertSelectorHasText('ol.navigator-results-list li', 'Default Gate');
-    });
-
-    casper.waitForSelector('div.navigator-header', function() {
-      test.assertSelectorHasText('div.navigator-header h1', 'Default Gate');
-    });
-
-    casper.waitForSelector('table.quality-gate-conditions tbody tr:nth-child(9)', function() {
-      test.assertElementCount('table.quality-gate-conditions tbody tr', 9);
-    });
-  });
-
-  casper.run(function() {
-    test.done();
-  });
+casper.test.begin('Quality Gates', function suite (test) {
+  casper
+      .start(lib.buildUrl('quality-gates'), function () {
+        lib.setDefaultViewport();
+
+        lib.mockRequest('/api/l10n/index', '{}');
+        lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
+        lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
+        lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
+      })
+
+      .then(function () {
+        casper.waitForSelector('.active', function () {
+          test.assertElementCount('.active', 1);
+          test.assertSelectorHasText('.search-navigator-side .active', 'Default Gate');
+        });
+      })
+
+      .then(function () {
+        casper.waitForSelector('.search-navigator-workspace-header', function () {
+          test.assertSelectorHasText('.search-navigator-workspace-header', 'Default Gate');
+        });
+      })
+
+      .then(function () {
+        casper.waitForSelector('table.quality-gate-conditions tbody tr:nth-child(9)', function () {
+          test.assertElementCount('table.quality-gate-conditions tbody tr', 9);
+        });
+      })
+
+      .run(function () {
+        test.done();
+      });
 });
index 1a10f0b5b34cbd313471aceedd30b219263a9286..ac1a4871c1b9efa29a0ed5da82e87e88403a03fa 100644 (file)
@@ -7,5 +7,4 @@ block header
 
 block body
   #content
-    #quality-gate-page-loader.navigator-page-loader
-      i.spinner
+    .search-navigator#quality-gates
index 5ebfed40fa8e6d65eb692c33257d9a215d5e035f..418fe0a78961023f1bd0f7b1309a5d6b23d39ab8 100644 (file)
@@ -101,164 +101,6 @@ a.active-link {
 
 
 
-/*
- * Inputs
- */
-
-input[type=text],
-input[type=password],
-input[type=email],
-input[type=search],
-textarea {
-  border: 1px solid @darkGrey;
-  .box-sizing(border-box);
-  background: #fff;
-  color: @baseFontColor;
-  .trans(border-color);
-
-  &:active,
-  &:focus {
-    border-color: @highlighted;
-    box-shadow: none;
-    outline: none;
-  }
-
-  &.invalid { border-color: @red; }
-}
-
-input[type=text],
-input[type=password],
-input[type=email],
-input[type=search] {
-  height: @formControlHeight;
-  padding: 0 3px;
-}
-
-input[type=search] {
-  -webkit-appearance: none;
-}
-
-textarea {
-  padding: 3px;
-}
-
-button,
-.button,
-input[type=submit],
-input[type=button] {
-  display: inline-block;
-  vertical-align: baseline;
-  height: @formControlHeight;
-  margin: 0 1px;
-  padding: 0 10px;
-
-  border: 1px solid @darkGrey;
-  .box-sizing(border-box);
-
-  background: #f4f4f4;
-
-  color: @baseFontColor;
-  font-weight: bold;
-  font-size: @baseFontSize;
-  text-align: center;
-  text-decoration: none;
-
-  cursor: pointer;
-  outline: none;
-  .trans(border-color);
-
-  &:hover, &.active {
-    border-color: #5281a0;
-    background: #4b9fd5;
-    color: #fff;
-  }
-
-  &:active {
-    border-color: #2790c0;
-    background: #78bdea;
-    color: #fff;
-  }
-
-  &:focus {
-    border-color: @highlighted;
-  }
-
-  &[disabled],
-  &[disabled]:hover,
-  &[disabled]:active,
-  &[disabled]:focus {
-    color: #bbb;
-    border-color: #ddd;
-    background: #ebebeb;
-    cursor: default;
-  }
-}
-
-.button { line-height: @formControlHeight; }
-
-.button-red {
-  &:hover, &:focus {
-    border-color: #900;
-    background: lighten(#900, 10%);
-    color: #fff;
-  }
-
-  &:active {
-    border-color: #900;
-    background: lighten(#900, 20%);
-  }
-}
-
-.button-clean,
-.button-clean:hover,
-.button-clean:focus {
-  margin: 0;
-  padding: 0;
-  border: none;
-  background: transparent;
-  color: @baseFontColor;
-}
-
-.button-group {
-  display: inline-block;
-  vertical-align: middle;
-  font-size: 0;
-  white-space: nowrap;
-
-  & > button,
-  & > .button {
-    position: relative;
-    z-index: 2;
-    display: inline-block;
-    vertical-align: middle;
-    margin: 0;
-    padding: 2px 8px;
-    font-size: @smallFontSize;
-    font-weight: normal;
-    cursor: pointer;
-
-    &:hover, &:focus, &:active, &.active {
-      z-index: 3;
-    }
-  }
-
-  & > .button { line-height: 16px; }
-
-  & > button + button,
-  & > button + .button,
-  & > .button + button,
-  & > .button + .button {
-    margin-left: -1px;
-  }
-
-  & > a:not(.button) {
-    vertical-align: middle;
-    margin: 0 8px;
-    font-size: @smallFontSize;
-  }
-}
-
-
 .message-notice {
   display: block;
   padding: 5px 8px;
index 86dd9fc5b3af4bda37613fb6d1ba8e5399e81af2..c2fe90522040c1c80d7c41f5ecf81bc7a3a45eb8 100644 (file)
@@ -3,5 +3,6 @@
 @import "init/links";
 @import "init/tables";
 @import "init/lists";
+@import "init/forms";
 @import "init/icons";
 @import "init/misc";
diff --git a/server/sonar-web/src/main/less/init/forms.less b/server/sonar-web/src/main/less/init/forms.less
new file mode 100644 (file)
index 0000000..9f8be90
--- /dev/null
@@ -0,0 +1,164 @@
+@import (reference) "../variables";
+@import (reference) "../mixins";
+@import (reference) "../components/ui";
+
+/*
+ * Inputs
+ */
+
+input[type=text],
+input[type=password],
+input[type=email],
+input[type=search],
+textarea {
+  border: 1px solid @darkGrey;
+  .box-sizing(border-box);
+  background: #fff;
+  color: @baseFontColor;
+  .trans(border-color);
+
+  &:active,
+  &:focus {
+    border-color: @highlighted;
+    box-shadow: none;
+    outline: none;
+  }
+
+  &.invalid { border-color: @red; }
+}
+
+input[type=text],
+input[type=password],
+input[type=email],
+input[type=search] {
+  height: @formControlHeight;
+  padding: 0 3px;
+}
+
+input[type=search] {
+  -webkit-appearance: none;
+}
+
+textarea {
+  padding: 3px;
+}
+
+button,
+.button,
+input[type=submit],
+input[type=button] {
+  display: inline-block;
+  vertical-align: baseline;
+  height: @formControlHeight;
+  margin: 0 1px;
+  padding: 0 10px;
+
+  border: 1px solid @darkGrey;
+  .box-sizing(border-box);
+
+  background: #f4f4f4;
+
+  color: @baseFontColor;
+  font-weight: bold;
+  font-size: @baseFontSize;
+  text-align: center;
+  text-decoration: none;
+
+  cursor: pointer;
+  outline: none;
+  .trans(border-color);
+
+  &:hover, &.active {
+    border-color: #5281a0;
+    background: #4b9fd5;
+    color: #fff;
+  }
+
+  &:active {
+    border-color: #2790c0;
+    background: #78bdea;
+    color: #fff;
+  }
+
+  &:focus {
+    border-color: @highlighted;
+  }
+
+  &[disabled],
+  &[disabled]:hover,
+  &[disabled]:active,
+  &[disabled]:focus {
+    color: #bbb;
+    border-color: #ddd;
+    background: #ebebeb;
+    cursor: default;
+  }
+}
+
+.button { line-height: @formControlHeight; }
+
+.button-red {
+  &:hover, &:focus {
+    border-color: #900;
+    background: lighten(#900, 10%);
+    color: #fff;
+  }
+
+  &:active {
+    border-color: #900;
+    background: lighten(#900, 20%);
+  }
+}
+
+.button-clean,
+.button-clean:hover,
+.button-clean:focus {
+  margin: 0;
+  padding: 0;
+  border: none;
+  background: transparent;
+  color: @baseFontColor;
+}
+
+.button-group {
+  display: inline-block;
+  vertical-align: middle;
+  font-size: 0;
+  white-space: nowrap;
+
+  & > button,
+  & > .button {
+    position: relative;
+    z-index: 2;
+    display: inline-block;
+    vertical-align: middle;
+    margin: 0;
+    padding: 2px 8px;
+    font-size: @smallFontSize;
+    font-weight: normal;
+    cursor: pointer;
+
+    &:hover, &:focus, &:active, &.active {
+      z-index: 3;
+    }
+  }
+
+  & > .button { line-height: 16px; }
+
+  & > button + button,
+  & > button + .button,
+  & > .button + button,
+  & > .button + .button {
+    margin-left: -1px;
+  }
+
+  & > a:not(.button) {
+    vertical-align: middle;
+    margin: 0 8px;
+    font-size: @smallFontSize;
+  }
+}
+
+.input-small {
+  width: 80px;
+}
index 9734d8f5b8eb2f4404e14f9e2607c1e2013d4ac3..eea7e0bf0d0e455b11751b1c9224839bee871632 100644 (file)
@@ -2,6 +2,14 @@
 @import (reference) "../mixins";
 @import (reference) "../components/navigator/config";
 
+.quality-gates-side {
+  background-color: @barBackgroundColor;
+}
+
+.quality-gates-results {
+  padding: 5px 0;
+}
+
 @qualityGateSidebarWidth: 230px;
 
 .quality-gates-navigator {
index 4a1ee1605e2e5bd8a1cf6b78e4314742ff1b3911..181ab0efae21a7b5b69841cc3dbd9a0c932e3fea 100644 (file)
@@ -2,6 +2,4 @@
   <script>require(['quality-gate/app']);</script>
 <% end %>
 
-<div id="quality-gate-page-loader" class="navigator-page-loader">
-  <i class="spinner"></i>
-</div>
+<div class="search-navigator" id="quality-gates"></div>