From 74400a889b2f8e8f3c6032130cadd8e9bb97c31e Mon Sep 17 00:00:00 2001 From: Tim Buckley Date: Tue, 22 Dec 2015 15:43:18 -0700 Subject: [PATCH] Add global resolution and grouping key options. This commit enhances the breadcrumb menu to add global selectors for resolution and grouping key into a single place on every page. Existing pages make use of and share these values as users browse between them. All existing UI elements for changing resolution or grouping parameters have also been removed in favor of this universal solution. Change-Id: Ifdef72219392c3afb979251d13863ca0146dde03 Co-Authored-By: Matthew Treinish --- app/js/controllers/grouped-runs.js | 15 +++- app/js/controllers/home.js | 35 ++++---- app/js/controllers/job.js | 8 +- app/js/controllers/test.js | 9 ++- app/js/directives/crumb-menu.js | 39 ++++++++- app/js/services/view.js | 42 ++++++++++ app/styles/_breadcrumbs.scss | 94 ++++++++++++++++++++++ app/styles/bootstrap/_variables.scss | 2 +- app/styles/main.scss | 1 + app/views/crumb-menu.html | 71 +++++++++++++--- app/views/grouped-runs.html | 2 +- app/views/home.html | 13 +-- app/views/job.html | 2 +- app/views/test.html | 2 +- test/unit/controllers/grouped_runs_spec.js | 7 +- test/unit/controllers/home_spec.js | 6 +- test/unit/controllers/job_spec.js | 9 ++- test/unit/controllers/tests_spec.js | 8 +- 18 files changed, 300 insertions(+), 65 deletions(-) create mode 100644 app/js/services/view.js create mode 100644 app/styles/_breadcrumbs.scss diff --git a/app/js/controllers/grouped-runs.js b/app/js/controllers/grouped-runs.js index bdff7455..4ed2f7d0 100644 --- a/app/js/controllers/grouped-runs.js +++ b/app/js/controllers/grouped-runs.js @@ -5,7 +5,10 @@ var controllersModule = require('./_index'); /** * @ngInject */ -function GroupedRunsController(pageTitleService, healthService, runMetadataKey, name, currentDate) { +function GroupedRunsController( + $scope, pageTitleService, healthService, viewService, + runMetadataKey, name, currentDate) { + // ViewModel var vm = this; @@ -17,6 +20,10 @@ function GroupedRunsController(pageTitleService, healthService, runMetadataKey, vm.runMetadataKey = decodeURIComponent(runMetadataKey); vm.name = decodeURIComponent(name); + // update the global grouping key - if we arrived here directly, it will not + // be set already + viewService.groupKey(runMetadataKey); + // Updates the page title based on the selected runMetadataKey pageTitleService.update(vm.runMetadataKey); @@ -105,13 +112,17 @@ function GroupedRunsController(pageTitleService, healthService, runMetadataKey, healthService.getRunsForRunMetadataKey(vm.runMetadataKey, vm.name, { start_date: startDate, stop_date: stopDate, - datetime_resolution: 'hour' + datetime_resolution: viewService.resolution().key }).then(function(response) { vm.processData(response.data); }); }; vm.loadData(); + + $scope.$on('view:resolution', function(event, resolution) { + vm.loadData(); + }); } controllersModule.controller('GroupedRunsController', GroupedRunsController); diff --git a/app/js/controllers/home.js b/app/js/controllers/home.js index 698b9afc..34ac05bf 100644 --- a/app/js/controllers/home.js +++ b/app/js/controllers/home.js @@ -5,7 +5,7 @@ var controllersModule = require('./_index'); /** * @ngInject */ -function HomeController(healthService, startDate, projectService) { +function HomeController($scope, healthService, startDate, projectService, viewService) { var byFailRateDesc = function(project1, project2) { // To get descending order, project2 should come first @@ -53,36 +53,33 @@ function HomeController(healthService, startDate, projectService) { }; }; - function loadRunMetadataKeys() { - healthService.getRunMetadataKeys().then(function(response) { - vm.runMetadataKeys = response.data; - }); - } - var loadData = function(runMetadataKey) { - var groupBy = runMetadataKey || vm.selectedRunMetadataKey; - vm.selectedRunMetadataKey = groupBy; - var start = new Date(startDate); start.setDate(start.getDate() - 20); - healthService.getRunsGroupedByMetadataPerDatetime(groupBy, { + healthService.getRunsGroupedByMetadataPerDatetime(vm.groupKey, { start_date: start, - datetime_resolution: 'hour' + datetime_resolution: viewService.resolution().key }).then(function(response) { - vm.processData(response.data); + processData(response.data); }); }; // ViewModel var vm = this; - vm.searchProject = ''; - vm.selectedRunMetadataKey = 'project'; - vm.loadRunMetadataKeys = loadRunMetadataKeys; - vm.processData = processData; vm.loadData = loadData; + vm.groupKey = viewService.groupKey(); + vm.searchProject = ''; - vm.loadData(); - vm.loadRunMetadataKeys(); + loadData(); + + $scope.$on('view:groupKey', function(event, groupKey) { + vm.groupKey = groupKey; + loadData(groupKey); + }); + + $scope.$on('view:resolution', function(event, resolution) { + loadData(); + }); } controllersModule.controller('HomeController', HomeController); diff --git a/app/js/controllers/job.js b/app/js/controllers/job.js index 4776cd79..801176d7 100644 --- a/app/js/controllers/job.js +++ b/app/js/controllers/job.js @@ -5,7 +5,7 @@ var controllersModule = require('./_index'); /** * @ngInject */ -function JobController(healthService, jobName, startDate) { +function JobController($scope, healthService, viewService, jobName, startDate) { // ViewModel var vm = this; @@ -141,13 +141,17 @@ function JobController(healthService, jobName, startDate) { healthService.getTestsFromBuildName(vm.name, { start_date: start, - datetime_resolution: 'hour' + datetime_resolution: viewService.resolution().key }).then(function(response) { vm.processData(response.data); }); }; vm.loadData(); + + $scope.$on('view:resolution', function(event, resolution) { + vm.loadData(); + }); } controllersModule.controller('JobController', JobController); diff --git a/app/js/controllers/test.js b/app/js/controllers/test.js index 21b319d4..59cb5ad3 100644 --- a/app/js/controllers/test.js +++ b/app/js/controllers/test.js @@ -5,7 +5,7 @@ var controllersModule = require('./_index'); /** * @ngInject */ -function TestController(healthService, testService, startDate, testId) { +function TestController($scope, healthService, testService, viewService, startDate, testId) { // ViewModel var vm = this; @@ -119,12 +119,17 @@ function TestController(healthService, testService, startDate, testId) { beginDate.setDate(startDate.getDate() - 15); healthService.getTestRunList(vm.testName, { start_date: beginDate, - stop_date: stopDate + stop_date: stopDate, + datetime_resolution: viewService.resolution().key }).then(function(response) { vm.processData(response.data); }); }; vm.loadData(); + + $scope.$on('view:resolution', function(event, resolution) { + vm.loadData(); + }); } controllersModule.controller('TestController', TestController); diff --git a/app/js/directives/crumb-menu.js b/app/js/directives/crumb-menu.js index 482d2433..c468167d 100644 --- a/app/js/directives/crumb-menu.js +++ b/app/js/directives/crumb-menu.js @@ -8,7 +8,37 @@ var directivesModule = require('./_index.js'); function crumbMenu() { var link = function(scope, element, attrs, ctrl, transclude) { transclude(function(clone) { - angular.element(element[0].querySelector('ol')).append(clone); + angular.element(element[0].querySelector('nav ul')).append(clone); + }); + }; + + /** + * @ngInject + */ + var controller = function($scope, healthService, viewService) { + $scope.resolutionOptions = viewService.resolutionOptions(); + $scope.selectedResolution = viewService.resolution(); + $scope.selectedGroupKey = viewService.groupKey(); + $scope.groupKeys = []; + + $scope.setResolution = function(resolution) { + viewService.resolution(resolution); + }; + + $scope.setGroupKey = function(groupKey) { + viewService.groupKey(groupKey); + }; + + $scope.$on('view:resolution', function(event, resolution) { + $scope.selectedResolution = resolution; + }); + + $scope.$on('view:groupKey', function(event, groupKey) { + $scope.selectedGroupKey = groupKey; + }); + + healthService.getRunMetadataKeys().then(function(response) { + $scope.groupKeys = response.data; }); }; @@ -16,7 +46,12 @@ function crumbMenu() { restrict: 'E', transclude: true, templateUrl: 'crumb-menu.html', - link: link + link: link, + controller: controller, + scope: { + 'showGroupKey': '@', + 'showResolution': '@' + } }; } diff --git a/app/js/services/view.js b/app/js/services/view.js new file mode 100644 index 00000000..5829ee57 --- /dev/null +++ b/app/js/services/view.js @@ -0,0 +1,42 @@ +'use strict'; + +var servicesModule = require('./_index.js'); + +/** + * @ngInject + */ +var viewService = function($rootScope) { + var resolutionOptions = [ + { name: 'Second', key: 'sec'}, + { name: 'Minute', key: 'min' }, + { name: 'Hour', key: 'hour' }, + { name: 'Day', key: 'day' } + ]; + var resolution = resolutionOptions[2]; + var groupKey = 'project'; + + return { + resolution: function(res) { + if (arguments.length === 1) { + resolution = res; + $rootScope.$broadcast('view:resolution', res); + } + + return resolution; + }, + resolutionOptions: function() { + return resolutionOptions; + }, + + groupKey: function(key) { + if (arguments.length === 1) { + groupKey = key; + $rootScope.$broadcast('view:groupKey', groupKey); + } + + return groupKey; + } + }; +}; + +servicesModule.factory('viewService', viewService); diff --git a/app/styles/_breadcrumbs.scss b/app/styles/_breadcrumbs.scss new file mode 100644 index 00000000..1f299833 --- /dev/null +++ b/app/styles/_breadcrumbs.scss @@ -0,0 +1,94 @@ +.nav-breadcrumb > li:not(:last-child):after { + content: " "; + display: block; + width: 0; + height: 0; + border-top: 17px solid transparent; + border-bottom: 17px solid transparent; + border-left: 10px solid #f8f8f8; + position: absolute; + top: 50%; + margin-top: -17px; + left: 100%; + z-index: 3; +} + +.nav-breadcrumb > li:not(:last-child):before { + content: " "; + display: block; + width: 0; + height: 0; + border-top: 17px solid transparent; + border-bottom: 17px solid transparent; + border-left: 10px solid rgb(173, 173, 173); + position: absolute; + top: 50%; + margin-top: -17px; + margin-left: 1px; + left: 100%; + z-index: 3; +} + +.navbar-breadcrumb { + min-height: 10px; +} +.navbar-breadcrumb ul:not(.nav-breadcrumb) > li > a { + padding: 6px 10px; +} +.nav-breadcrumb > li { + padding:6px 12px 6px 24px; +} +.nav-breadcrumb > li > a { + padding: 0px; +} +.nav-breadcrumb li:not(:last-child):hover { + background-color: #ebebeb; +} + +.nav-breadcrumb > li:first-child { + padding:6px 6px 6px 10px; +} +.btn-breadcrumb > li:last-child { + padding:6px 18px 6px 24px; +} + +.nav-breadcrumb > li:not(:last-child):after { + border-left: 10px solid #f8f8f8; +} +.nav-breadcrumb > li:not(:last-child):before { + border-left: 10px solid #ccc; +} +.nav-breadcrumb > li:hover:not(:last-child):after { + border-left: 10px solid #ebebeb; +} +.nav-breadcrumb > li:hover:not(:last-child):before { + border-left: 10px solid #adadad; +} + +.navbar-form-xs { + padding: 0px 20px; + margin: 5px 8px; +} + +.input-xs { + height: 22px; + padding: 0px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.navbar-breadcrumb .container-fluid li[dropdown]:not(:last-child):before { + background: #ebebeb; + width: 1px; + content: ""; + display:block; + position: absolute; + top:0; + bottom: 0; + right: 0; +} + +.navbar-breadcrumb ul.dropdown-menu a { + cursor: pointer; +} diff --git a/app/styles/bootstrap/_variables.scss b/app/styles/bootstrap/_variables.scss index bf9373b9..d472dbbe 100644 --- a/app/styles/bootstrap/_variables.scss +++ b/app/styles/bootstrap/_variables.scss @@ -332,7 +332,7 @@ $grid-columns: 12 !default; $grid-gutter-width: 30px !default; // Navbar collapse //** Point at which the navbar becomes uncollapsed. -$grid-float-breakpoint: $screen-sm-min !default; +$grid-float-breakpoint: 0px !default; //** Point at which the navbar begins collapsing. $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default; diff --git a/app/styles/main.scss b/app/styles/main.scss index 9b958556..473cfeac 100644 --- a/app/styles/main.scss +++ b/app/styles/main.scss @@ -6,3 +6,4 @@ @import 'nv.d3'; @import 'header'; @import 'footer'; +@import 'breadcrumbs' diff --git a/app/views/crumb-menu.html b/app/views/crumb-menu.html index 75db1770..f5fbef5c 100644 --- a/app/views/crumb-menu.html +++ b/app/views/crumb-menu.html @@ -1,17 +1,62 @@ - + + diff --git a/app/views/grouped-runs.html b/app/views/grouped-runs.html index 1ac59718..f64337f4 100644 --- a/app/views/grouped-runs.html +++ b/app/views/grouped-runs.html @@ -1,7 +1,7 @@

{{ groupedRuns.name }} jobs

- +
  • Project: {{ groupedRuns.name }}
  • diff --git a/app/views/home.html b/app/views/home.html index 0fe4444a..38700464 100644 --- a/app/views/home.html +++ b/app/views/home.html @@ -3,7 +3,7 @@

    OpenStack Health

    - +
    @@ -36,15 +36,6 @@
    -
    - - -
    @@ -56,7 +47,7 @@
    diff --git a/app/views/job.html b/app/views/job.html index fa206583..bb62b426 100644 --- a/app/views/job.html +++ b/app/views/job.html @@ -1,7 +1,7 @@

    {{ job.name }} tests

    - +
  • Job: {{ job.name }}
  • diff --git a/app/views/test.html b/app/views/test.html index 9bf66459..b8b70427 100644 --- a/app/views/test.html +++ b/app/views/test.html @@ -1,7 +1,7 @@

    {{ testCtrl.testShortName }}

    - +
  • Test: {{testCtrl.testShortName }}
  • diff --git a/test/unit/controllers/grouped_runs_spec.js b/test/unit/controllers/grouped_runs_spec.js index 86cdb259..68affdc4 100644 --- a/test/unit/controllers/grouped_runs_spec.js +++ b/test/unit/controllers/grouped_runs_spec.js @@ -4,11 +4,12 @@ describe('GroupedRunsController', function() { module('app.controllers'); }); - var $httpBackend, $controller, healthService; + var $scope, $httpBackend, $controller, healthService; var API_ROOT = 'http://8.8.4.4:8080'; var DEFAULT_CURRENT_DATE = new Date(); - beforeEach(inject(function(_$httpBackend_, _$controller_, _healthService_) { + beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) { + $scope = $rootScope.$new(); $httpBackend = _$httpBackend_; mockConfigService(); mockHealthService(); @@ -78,6 +79,7 @@ describe('GroupedRunsController', function() { it('should process chart data correctly', function() { var groupedRunsController = $controller('GroupedRunsController', { + $scope: $scope, healthService: healthService, runMetadataKey: 'project', name: 'openstack/cinder', @@ -104,6 +106,7 @@ describe('GroupedRunsController', function() { it('should process chart data rate correctly', function() { var groupedRunsController = $controller('GroupedRunsController', { + $scope: $scope, healthService: healthService, runMetadataKey: 'project', name: 'openstack/cinder', diff --git a/test/unit/controllers/home_spec.js b/test/unit/controllers/home_spec.js index d0bb1f79..eb33ac68 100644 --- a/test/unit/controllers/home_spec.js +++ b/test/unit/controllers/home_spec.js @@ -3,7 +3,7 @@ describe('HomeController', function() { module('app'); }); - var $controller, homeController, projectService; + var $scope, $controller, homeController, projectService; var mockResponse = { data: {} }; var mockMetadataKeysResponse = { data: { @@ -15,7 +15,8 @@ describe('HomeController', function() { } }; - beforeEach(inject(function(_$controller_) { + beforeEach(inject(function($rootScope, _$controller_) { + $scope = $rootScope.$new(); $controller = _$controller_; var defaultStartDate = new Date(); @@ -35,6 +36,7 @@ describe('HomeController', function() { }; homeController = $controller('HomeController', { + $scope: $scope, healthService: healthService, startDate: defaultStartDate, projectService: projectService diff --git a/test/unit/controllers/job_spec.js b/test/unit/controllers/job_spec.js index e573f56f..a3c7ac88 100644 --- a/test/unit/controllers/job_spec.js +++ b/test/unit/controllers/job_spec.js @@ -4,17 +4,17 @@ describe('JobController', function() { module('app.controllers'); }); - var $httpBackend, $controller, healthService; + var $scope, $httpBackend, $controller, healthService; var API_ROOT = 'http://8.8.4.4:8080'; var DEFAULT_START_DATE = new Date(); - beforeEach(inject(function(_$httpBackend_, _$controller_, _healthService_) { + beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) { $httpBackend = _$httpBackend_; mockConfigService(); mockHealthService(); + $scope = $rootScope.$new(); $controller = _$controller_; - healthService = _healthService_; })); @@ -64,6 +64,7 @@ describe('JobController', function() { it('should process chart data correctly', function() { var jobController = $controller('JobController', { + $scope: $scope, healthService: healthService, jobName: 'gate-tempest-dsvm-neutron-full', startDate: DEFAULT_START_DATE @@ -97,6 +98,7 @@ describe('JobController', function() { it('should process chart data rate correctly', function() { var jobController = $controller('JobController', { + $scope: $scope, healthService: healthService, jobName: 'gate-tempest-dsvm-neutron-full', startDate: DEFAULT_START_DATE @@ -115,6 +117,7 @@ describe('JobController', function() { it('should process tests correctly', function() { var jobController = $controller('JobController', { + $scope: $scope, healthService: healthService, jobName: 'gate-tempest-dsvm-neutron-full', startDate: DEFAULT_START_DATE diff --git a/test/unit/controllers/tests_spec.js b/test/unit/controllers/tests_spec.js index 14fbb558..291c3427 100644 --- a/test/unit/controllers/tests_spec.js +++ b/test/unit/controllers/tests_spec.js @@ -4,16 +4,17 @@ describe('TestsController', function() { module('app.controllers'); }); - var $httpBackend, $controller, healthService; + var $scope, $httpBackend, $controller, healthService; var API_ROOT = 'http://8.8.4.4:8080'; var DEFAULT_START_DATE = new Date(); - beforeEach(inject(function(_$httpBackend_, _$controller_, _healthService_) { + beforeEach(inject(function($rootScope, _$httpBackend_, _$controller_, _healthService_) { $httpBackend = _$httpBackend_; mockConfigService(); mockHealthService(); + $scope = $rootScope.$new(); $controller = _$controller_; healthService = _healthService_; })); @@ -61,7 +62,8 @@ describe('TestsController', function() { it('should process chart data correctly', function() { var testsController = $controller('TestsController', { - healthService: healthService + healthService: healthService, + $scope: $scope }); $httpBackend.flush();