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
-
-
+
+
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 @@
+
+
+
+
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 ====================================================================