Skip to content

Commit

Permalink
using ui-select for field selection in visualize (#10998)
Browse files Browse the repository at this point in the history
* using ui-select for field selection in visualize

* adding a limit and infinite scroll

* fixing based on CJ's comments

* Add sort prefix first utility

* Add sortPrefixFirst tests

* updating uiSelect to use the new filter

* rebasing on master

* adding uiSelect to top hits sort-on input

* fixing tests
  • Loading branch information
ppisljar committed May 5, 2017
1 parent 055e99e commit 815b082
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,29 @@
padding: @vis-editor-agg-editor-spacing;
}

/**
* 1. Make ui-select input match other inputs.
* 2. Show invalid state if the user has interacted with the input without selecting an option.
*/
.vis-editor-field-ui-select {
.ui-select-match {
.btn {
border: 2px solid #ecf0f1; /* 1 */
background-color: white; /* 1 */
color: black; /* 1 */
}
}

&.ng-invalid.ng-dirty,
&.ng-invalid.ng-touched {
.ui-select-match {
.btn {
border-color: red; /* 2 */
}
}
}
}

.vis-editor-canvas {
flex: 1 0 (@screen-md-min - @vis-editor-sidebar-basis);
display: flex;
Expand Down
2 changes: 2 additions & 0 deletions src/core_plugins/kibana/public/visualize/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import 'ui/draggable/draggable_item';
import 'ui/draggable/draggable_handle';
import 'plugins/kibana/visualize/saved_visualizations/_saved_vis';
import 'plugins/kibana/visualize/saved_visualizations/saved_visualizations';
import 'ui/directives/scroll_bottom';
import 'ui/filters/sort_prefix_first';
import uiRoutes from 'ui/routes';
import visualizeListingTemplate from './listing/visualize_listing.html';
import { VisualizeListingController } from './listing/visualize_listing';
Expand Down
25 changes: 18 additions & 7 deletions src/ui/public/agg_types/controls/field.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,27 @@
</p>
</div>

<select
class="form-control"

<ui-select
name="field"
required
ng-model="agg.params.field"
class="vis-editor-field-ui-select"
ng-show="indexedFields.length"
auto-select-if-only-one="indexedFields"
ng-options="field as field.displayName group by field.type for field in indexedFields"
ng-change="aggParam.onChange(agg)">
</select>
ng-model="agg.params.field"
on-select="aggParam.onChange(agg)"
uis-open-close="limit = 100"
>
<ui-select-match placeholder="Select a field">
{{$select.selected.displayName}}
</ui-select-match>
<ui-select-choices
group-by="'type'"
kbn-scroll-bottom="limit = limit + 100"
repeat="field in indexedFields | filter: { displayName: $select.search } | sortPrefixFirst:$select.search:'name' | limitTo: limit"
>
<div ng-bind-html="field.displayName | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>

<div class="hintbox" ng-if="!indexedFields.length">
<p>
Expand Down
25 changes: 18 additions & 7 deletions src/ui/public/agg_types/controls/top_sort.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,26 @@
Sort On
</label>

<select
class="form-control"
name="sortField"
required
ng-model="agg.params.sortField"
<ui-select
name="field"
sortField
class="vis-editor-field-ui-select"
ng-show="sortFieldOptions.length"
ng-model="agg.params.sortField"
uis-open-close="limit = 100"
auto-select-if-only-one="sortFieldOptions"
ng-options="field as field.displayName group by field.type for field in sortFieldOptions">
</select>
>
<ui-select-match placeholder="Select a field">
{{$select.selected.displayName}}
</ui-select-match>
<ui-select-choices
group-by="'type'"
kbn-scroll-bottom="limit = limit + 100"
repeat="field in sortFieldOptions | filter: { displayName: $select.search } | sortPrefixFirst:$select.search:'name' | limitTo: limit"
>
<div ng-bind-html="field.displayName | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
</div>

<div class="form-group">
Expand Down
38 changes: 38 additions & 0 deletions src/ui/public/directives/scroll_bottom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { uiModules } from 'ui/modules';
const module = uiModules.get('kibana');

module.directive('kbnScrollBottom', function () {
return {
restrict: 'A',
link: function ($scope, $element, attr) {
let checkTimer;

function onScroll() {
const position = $element.scrollTop() + $element.outerHeight();
const height = $element[0].scrollHeight;
const remaining = height - position;
const margin = 50;

if (!height || !position) return;
if (remaining <= margin) {
$scope.$eval(attr.kbnScrollBottom);
}
}

function scheduleCheck() {
if (checkTimer) return;
checkTimer = setTimeout(function () {
checkTimer = null;
onScroll();
}, 50);
}

$element.on('scroll', scheduleCheck);
$scope.$on('$destroy', function () {
clearTimeout(checkTimer);
$element.off('scroll', scheduleCheck);
});
scheduleCheck();
}
};
});
8 changes: 8 additions & 0 deletions src/ui/public/filters/sort_prefix_first.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { uiModules } from 'ui/modules';
import { sortPrefixFirst } from '../utils/sort_prefix_first';

uiModules
.get('kibana')
.filter('sortPrefixFirst', function () {
return sortPrefixFirst;
});
35 changes: 35 additions & 0 deletions src/ui/public/utils/__tests__/sort_prefix_first.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import expect from 'expect.js';
import { sortPrefixFirst } from '../sort_prefix_first';

describe('sortPrefixFirst', function () {
it('should return the original unmodified array if no prefix is provided', function () {
const array = ['foo', 'bar', 'baz'];
const result = sortPrefixFirst(array);
expect(result).to.be(array);
expect(result).to.eql(['foo', 'bar', 'baz']);
});

it('should sort items that match the prefix first without modifying the original array', function () {
const array = ['foo', 'bar', 'baz'];
const result = sortPrefixFirst(array, 'b');
expect(result).to.not.be(array);
expect(result).to.eql(['bar', 'baz', 'foo']);
expect(array).to.eql(['foo', 'bar', 'baz']);
});

it('should not modify the order of the array other than matching prefix without modifying the original array', function () {
const array = ['foo', 'bar', 'baz', 'qux', 'quux'];
const result = sortPrefixFirst(array, 'b');
expect(result).to.not.be(array);
expect(result).to.eql(['bar', 'baz', 'foo', 'qux', 'quux']);
expect(array).to.eql(['foo', 'bar', 'baz', 'qux', 'quux']);
});

it('should sort objects by property if provided', function () {
const array = [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }, { name: 'qux' }, { name: 'quux' }];
const result = sortPrefixFirst(array, 'b', 'name');
expect(result).to.not.be(array);
expect(result).to.eql([{ name: 'bar' }, { name: 'baz' }, { name: 'foo' }, { name: 'qux' }, { name: 'quux' }]);
expect(array).to.eql([{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }, { name: 'qux' }, { name: 'quux' }]);
});
});
16 changes: 16 additions & 0 deletions src/ui/public/utils/sort_prefix_first.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function sortPrefixFirst(array, prefix, property) {
if (!prefix) return array;
return [...array].sort(sortPrefixFirstComparator);

function sortPrefixFirstComparator(a, b) {
const aValue = property ? a[property] : a;
const bValue = property ? b[property] : b;

const bothStartWith = aValue.startsWith(prefix) && bValue.startsWith(prefix);
const neitherStartWith = !aValue.startsWith(prefix) && !bValue.startsWith(prefix);

if (bothStartWith || neitherStartWith) return 0;
if (aValue.startsWith(prefix)) return -1;
else return 1;
}
}
15 changes: 10 additions & 5 deletions test/functional/page_objects/visualize_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,22 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
getField() {
return remote
.setFindTimeout(defaultFindTimeout)
.findByCssSelector('.ng-valid-required[name="field"] option[selected="selected"]')
.findByCssSelector('.ng-valid-required[name="field"] .ui-select-match-text')
.getVisibleText();
}

selectField(fieldValue, groupName = 'buckets') {
return retry.try(function tryingForTime() {
return remote
.setFindTimeout(defaultFindTimeout)
// the css below should be more selective
.findByCssSelector(`[group-name="${groupName}"] option[label="${fieldValue}"]`)
.click();
.setFindTimeout(defaultFindTimeout)
.findByCssSelector(`[group-name="${groupName}"] .ui-select-container`)
.click()
.then(() => {
return remote
.findByCssSelector(`[group-name="${groupName}"] input.ui-select-search`)
.type(fieldValue)
.pressKeys('\uE006');
});
});
}

Expand Down

0 comments on commit 815b082

Please sign in to comment.