diff --git a/src/plugins/kibana/public/visualize/styles/main.less b/src/plugins/kibana/public/visualize/styles/main.less index b0b18de2a573..cfbd3addd324 100644 --- a/src/plugins/kibana/public/visualize/styles/main.less +++ b/src/plugins/kibana/public/visualize/styles/main.less @@ -13,6 +13,14 @@ padding: 0; display: flex; + div.wizard-small { + flex: 2; + } + + div.wizard-large { + flex: 3; + } + .wizard-column { flex: 1; display: flex; @@ -45,11 +53,6 @@ .list-group { margin-bottom: 0; - - .list-group-item { - border-radius: 0; - border: none; - } } .striped { diff --git a/src/plugins/kibana/public/visualize/wizard/step_2.html b/src/plugins/kibana/public/visualize/wizard/step_2.html index e8cf5964b655..bc49fab790d9 100644 --- a/src/plugins/kibana/public/visualize/wizard/step_2.html +++ b/src/plugins/kibana/public/visualize/wizard/step_2.html @@ -1,20 +1,15 @@
-
-

From a New Search

- -
-
-
Index Patterns
-
- -
+
+

From a New Search, Select Index

+ +
-
+

Or, From a Saved Search

{ + return _.isPlainObject(item); + }); + + var init = function (arr, willFail) { + // Load the application + ngMock.module('kibana'); + + // Create the scope + ngMock.inject(function ($rootScope, $compile) { + $scope = $rootScope.$new(); + $scope.perPage = 5; + $scope.list = list; + $scope.listProperty = isArrayOfObjects ? 'title' : undefined; + $scope.test = function (val) { return val; }; + + // Create the element + if (willFail) { + $element = angular.element(''); + } else { + $element = angular.element(''); + } + + // And compile it + $compile($element)($scope); + + // Fire a digest cycle + $element.scope().$digest(); + + // Grab the isolate scope so we can test it + $isolatedScope = $element.isolateScope(); + }); + }; + + describe('paginatedSelectableList', function () { + it('should throw an error when there is no makeUrl and onSelect attribute', ngMock.inject(function ($compile, $rootScope) { + function errorWrapper() { + $compile(angular.element(''))($rootScope.new()); + } + expect(errorWrapper).to.throwError(); + })); + + it('should throw an error with both makeUrl and onSelect attributes', function () { + function errorWrapper() { + init(list, true); + } + expect(errorWrapper).to.throwError(); + }); + + describe('$scope.hits', function () { + beforeEach(function () { + init(list); + }); + + it('should initially sort an array of objects in ascending order', function () { + var property = $isolatedScope.listProperty; + var sortedList = property ? _.sortBy(list, property) : _.sortBy(list); + + expect($isolatedScope.hits).to.be.an('array'); + + $isolatedScope.hits.forEach(function (hit, index) { + if (property) { + expect(hit[property]).to.equal(sortedList[index][property]); + } else { + expect(hit).to.equal(sortedList[index]); + } + }); + }); + }); + + describe('$scope.sortHits', function () { + beforeEach(function () { + init(list); + }); + + it('should sort an array of objects in ascending order', function () { + var property = $isolatedScope.listProperty; + var sortedList = property ? _.sortBy(list, property) : _.sortBy(list); + + $isolatedScope.isAscending = false; + $isolatedScope.sortHits(list); + + expect($isolatedScope.isAscending).to.be(true); + + $isolatedScope.hits.forEach(function (hit, index) { + if (property) { + expect(hit[property]).to.equal(sortedList[index][property]); + } else { + expect(hit).to.equal(sortedList[index]); + } + }); + }); + + it('should sort an array of objects in descending order', function () { + var property = $isolatedScope.listProperty; + var reversedList = property ? _.sortBy(list, property).reverse() : _.sortBy(list).reverse(); + + $isolatedScope.isAscending = true; + $isolatedScope.sortHits(list); + + expect($isolatedScope.isAscending).to.be(false); + + $isolatedScope.hits.forEach(function (hit, index) { + if (property) { + expect(hit[property]).to.equal(reversedList[index][property]); + } else { + expect(hit).to.equal(reversedList[index]); + } + }); + }); + }); + + describe('$scope.makeUrl', function () { + beforeEach(function () { + init(list); + }); + + it('should return the result of the function its passed', function () { + var property = $isolatedScope.listProperty; + var sortedList = property ? _.sortBy(list, property) : _.sortBy(list); + + $isolatedScope.hits.forEach(function (hit, index) { + if (property) { + expect($isolatedScope.makeUrl(hit)[property]).to.equal(sortedList[index][property]); + } else { + expect($isolatedScope.makeUrl(hit)).to.equal(sortedList[index]); + } + }); + }); + }); + + describe('$scope.onSelect', function () { + beforeEach(function () { + init(list); + }); + + it('should return the result of the function its passed', function () { + var property = $isolatedScope.listProperty; + var sortedList = property ? _.sortBy(list, property) : _.sortBy(list); + + $isolatedScope.userOnSelect = function (val) { return val; }; + + $isolatedScope.hits.forEach(function (hit, index) { + if (property) { + expect($isolatedScope.onSelect(hit)[property]).to.equal(sortedList[index][property]); + } else { + expect($isolatedScope.onSelect(hit)).to.equal(sortedList[index]); + } + }); + }); + }); + }); +}); diff --git a/src/ui/public/directives/paginate.js b/src/ui/public/directives/paginate.js index 1ffbf5e7468f..444df4b3f4b7 100644 --- a/src/ui/public/directives/paginate.js +++ b/src/ui/public/directives/paginate.js @@ -194,5 +194,3 @@ uiModules.get('kibana') template: paginateControlsTemplate }; }); - - diff --git a/src/ui/public/directives/paginated_selectable_list.js b/src/ui/public/directives/paginated_selectable_list.js new file mode 100644 index 000000000000..18d5ec3ab6e3 --- /dev/null +++ b/src/ui/public/directives/paginated_selectable_list.js @@ -0,0 +1,71 @@ +import _ from 'lodash'; +import uiModules from 'ui/modules'; +import paginatedSelectableListTemplate from 'ui/partials/paginated_selectable_list.html'; + +const module = uiModules.get('kibana'); + +function throwError(message) { + throw new Error(message); +} + +module.directive('paginatedSelectableList', function (kbnUrl) { + + return { + restrict: 'E', + scope: { + perPage: '=?', + list: '=', + listProperty: '=', + userMakeUrl: '=?', + userOnSelect: '=?' + }, + template: paginatedSelectableListTemplate, + controller: function ($scope, $element, $filter) { + // Should specify either user-make-url or user-on-select + if (!$scope.userMakeUrl && !$scope.userOnSelect) { + throwError('paginatedSelectableList directive expects a makeUrl or onSelect function'); + } + + // Should specify either user-make-url or user-on-select, but not both. + if ($scope.userMakeUrl && $scope.userOnSelect) { + throwError('paginatedSelectableList directive expects a makeUrl or onSelect attribute but not both'); + } + + $scope.perPage = $scope.perPage || 10; + $scope.hits = $scope.list = _.sortBy($scope.list, accessor); + $scope.hitCount = $scope.hits.length; + + /** + * Boolean that keeps track of whether hits are sorted ascending (true) + * or descending (false) + * * @type {Boolean} + */ + $scope.isAscending = true; + + /** + * Sorts saved object finder hits either ascending or descending + * @param {Array} hits Array of saved finder object hits + * @return {Array} Array sorted either ascending or descending + */ + $scope.sortHits = function (hits) { + const sortedList = _.sortBy(hits, accessor); + + $scope.isAscending = !$scope.isAscending; + $scope.hits = $scope.isAscending ? sortedList : sortedList.reverse(); + }; + + $scope.makeUrl = function (hit) { + return $scope.userMakeUrl(hit); + }; + + $scope.onSelect = function (hit, $event) { + return $scope.userOnSelect(hit, $event); + }; + + function accessor(val) { + const prop = $scope.listProperty; + return prop ? val[prop] : val; + } + } + }; +}); diff --git a/src/ui/public/partials/paginated_selectable_list.html b/src/ui/public/partials/paginated_selectable_list.html new file mode 100644 index 000000000000..8b9874bf4f32 --- /dev/null +++ b/src/ui/public/partials/paginated_selectable_list.html @@ -0,0 +1,43 @@ +
+
+
+
+ + + + +
+
+ {{ (hits | filter: query).length }} of {{ hitCount }} +
+
+
+
+ +
    +
  • + + Name + + +
  • +
  • + + {{ hit }} + +
    + {{ hit }} +
    +
  • +
  • +

    No matches found.

    +
  • +
+
diff --git a/src/ui/public/partials/saved_object_finder.html b/src/ui/public/partials/saved_object_finder.html index e5eda0c18cdf..11990711f350 100644 --- a/src/ui/public/partials/saved_object_finder.html +++ b/src/ui/public/partials/saved_object_finder.html @@ -8,7 +8,7 @@ {{finder.hitCount}} of {{finder.hitCount}}
diff --git a/src/ui/public/styles/base.less b/src/ui/public/styles/base.less index 62899e437691..4a97d0d8d407 100644 --- a/src/ui/public/styles/base.less +++ b/src/ui/public/styles/base.less @@ -320,7 +320,8 @@ bread-crumbs { } //== SavedObjectFinder -saved-object-finder { +saved-object-finder, +paginated-selectable-list { .row { background-color: @kibanaGray6; padding: 10px; @@ -328,6 +329,27 @@ saved-object-finder { flex-direction: row; } + .finder-hit-count, + .finder-manage-object { + min-width: 80px; + padding: 5px; + text-align: center; + } + + .finder-hit-count { + flex: 1; + + span { + color: @kibanaGray3; + } + } + + .finder-manage-object { + flex: 3; + text-align: left; + text-transform: capitalize; + } + .form-group { margin-bottom: 0; float: left; @@ -337,7 +359,6 @@ saved-object-finder { border: none; padding: 5px 0px; border-radius: @border-radius-base; - text-transform: capitalize; } span { @@ -350,30 +371,6 @@ saved-object-finder { width: 15px; } } - - .finder-hit-count, .finder-manage-object { - min-width: 80px; - padding: 5px; - } - - .finder-hit-count { - flex: 1; - text-align: center; - - span { - color: @kibanaGray3; - } - } - - .finder-manage-object { - flex: 3; - text-align: left; - text-transform: capitalize; - } - } - - .list-group-item-menu:hover { - background-color: transparent; } ul.li-striped { @@ -413,6 +410,7 @@ saved-object-finder { margin-right: 10px; } + display: block; color: @saved-object-finder-link-color !important; } @@ -464,6 +462,12 @@ saved-object-finder { } } } + + paginate { + paginate-controls { + margin: 20px; + } + } } // when rendered within a config dropdown, don't use a bottom margin diff --git a/src/ui/public/styles/dark-variables.less b/src/ui/public/styles/dark-variables.less index 3b07e8f8bf7b..e61a7c7a8898 100644 --- a/src/ui/public/styles/dark-variables.less +++ b/src/ui/public/styles/dark-variables.less @@ -156,4 +156,3 @@ @sidebar-bg: @btn-default-bg; @sidebar-hover-bg: darken(@btn-default-bg, 5%); @sidebar-hover-color: @text-color; - diff --git a/src/ui/public/styles/variables/for-theme.less b/src/ui/public/styles/variables/for-theme.less index 6aab3d69c557..ab9933ecdbc4 100644 --- a/src/ui/public/styles/variables/for-theme.less +++ b/src/ui/public/styles/variables/for-theme.less @@ -172,7 +172,7 @@ @list-group-menu-item-color: @link-color; @list-group-menu-item-select-color: @link-color; @list-group-menu-item-active-bg: @well-bg; -@list-group-menu-item-hover-bg: @well-bg; +@list-group-menu-item-hover-bg: @kibanaGray5; // Hint Box ====================================================================