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
# 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
jQuery.when(qualityGatesXHR, appXHR, l10nXHR)
.done ->
- # Remove the initial spinner
- jQuery('#quality-gate-page-loader').remove()
-
# Start the application
App.start()
'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()
@app.layout.detailsRegion.show qualityGateDetailView
qualityGateDetailView.$el.hide()
- qualityGateDetailHeaderView.showSpinner()
qualityGate.fetch().done ->
qualityGateDetailView.$el.show()
- qualityGateDetailHeaderView.hideSpinner()
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')
@changeDefault false
- showSpinner: ->
- @$el.hide()
- jQuery(@spinner).insertBefore @$el
-
-
- hideSpinner: ->
- @$el.prev().remove()
- @$el.show()
-
-
serializeData: ->
_.extend super, canEdit: @options.app.canEdit
], ->
class QualityGateSidebarListItemView extends Marionette.ItemView
- tagName: 'li'
+ className: 'facet search-navigator-facet'
template: Templates['quality-gate-sidebar-list-item']
) ->
class QualityGateSidebarListView extends Marionette.CollectionView
- tagName: 'ol'
- className: 'navigator-results-list'
+ className: 'search-navigator-facet-list'
itemView: QualityGateSidebarListItemView
emptyView: QualityGateSidebarListEmptyView
{{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}}
-<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}}
-<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}}
-<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>
+/* 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();
+ });
});
block body
#content
- #quality-gate-page-loader.navigator-page-loader
- i.spinner
+ .search-navigator#quality-gates
-/*
- * 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;
@import "init/links";
@import "init/tables";
@import "init/lists";
+@import "init/forms";
@import "init/icons";
@import "init/misc";
--- /dev/null
+@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;
+}
@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 {
<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>