Factor out column manipulation in the doc table (#11006)

Backports PR #10681

* Factor out column manipulation in the doc table

This refactoring effort turns the implicit manipulation of the set of
columns that was scattered throughout the doc table and the field
chooser into explicit function calls. This brings with it the following
improvements:

* The column manipulation code is not duplicated (DRY)
* The controller stays in control (IOC)
* If the required functions are not provided by the controller,
  manipulation of the columns is disabled.

Additionally, the `discover_field` now uses a properly isolated scope
instead of accessing inherited properties of the scope.

* Make filter addition and removal in tests more reliable

* Change function name to plural, move up ng-if

* Remove inconsistent variable initialization

* Fix function name typo

* Save the state in the action instead of a $watch
This commit is contained in:
Felix Stürmer 2017-04-04 01:02:09 +02:00 committed by GitHub
parent 10ac953d29
commit eb40f7d249
22 changed files with 319 additions and 119 deletions

View file

@ -21,11 +21,13 @@
<i class="fa fa-search-plus text-muted"></i>
<i class="fa fa-search-minus text-muted"></i>
</span>
<span ng-if="columns">
<i ng-click="toggleColumn(field)"
tooltip="Toggle column in table"
<span ng-if="canToggleColumns()">
<i
class="fa fa-columns"
ng-click="toggleColumn(field)"
tooltip-append-to-body="1"
class="fa fa-columns"></i>
tooltip="Toggle column in table"
></i>
</span>
<span ng-if="!indexPattern.metaFields.includes(field)">
<i ng-click="filter('_exists_', field, '+')"

View file

@ -13,7 +13,9 @@ docViewsRegistry.register(function () {
hit: '=',
indexPattern: '=',
filter: '=',
columns: '='
columns: '=',
onAddColumn: '=',
onRemoveColumn: '=',
},
controller: function ($scope) {
$scope.mapping = $scope.indexPattern.fields.byName;
@ -21,8 +23,19 @@ docViewsRegistry.register(function () {
$scope.formatted = $scope.indexPattern.formatHit($scope.hit);
$scope.fields = _.keys($scope.flattened).sort();
$scope.toggleColumn = function (fieldName) {
_.toggleInOut($scope.columns, fieldName);
$scope.canToggleColumns = function canToggleColumn() {
return (
_.isFunction($scope.onAddColumn)
&& _.isFunction($scope.onRemoveColumn)
);
};
$scope.toggleColumn = function toggleColumn(columnName) {
if ($scope.columns.includes(columnName)) {
$scope.onRemoveColumn(columnName);
} else {
$scope.onAddColumn(columnName);
}
};
$scope.showArrayInObjectsWarning = function (row, field) {

View file

@ -66,7 +66,10 @@
render-counter
class="panel-content"
filter="filter"
on-add-column="addColumn"
on-change-sort-order="setSortOrder"
on-move-column="moveColumn"
on-remove-column="removeColumn"
>
</doc-table>
</div>

View file

@ -1,6 +1,7 @@
import _ from 'lodash';
import 'ui/visualize';
import 'ui/doc_table';
import * as columnActions from 'ui/doc_table/actions/columns';
import 'plugins/kibana/dashboard/panel/get_object_loaders_for_dashboard';
import FilterManagerProvider from 'ui/filter_manager';
import uiModules from 'ui/modules';
@ -108,16 +109,27 @@ uiModules
$scope.panel.columns = $scope.panel.columns || $scope.savedObj.columns;
$scope.panel.sort = $scope.panel.sort || $scope.savedObj.sort;
// If the user updates the sort direction or columns in a saved search, we want to save that
// to the ui state so the share url will show our temporary modifications.
$scope.$watchCollection('panel.columns', function () {
$scope.saveState();
});
$scope.setSortOrder = function setSortOrder(columnName, direction) {
$scope.panel.sort = [columnName, direction];
$scope.saveState();
};
$scope.addColumn = function addColumn(columnName) {
$scope.savedObj.searchSource.get('index').popularizeField(columnName, 1);
columnActions.addColumn($scope.panel.columns, columnName);
$scope.saveState(); // sync to sharing url
};
$scope.removeColumn = function removeColumn(columnName) {
$scope.savedObj.searchSource.get('index').popularizeField(columnName, 1);
columnActions.removeColumn($scope.panel.columns, columnName);
$scope.saveState(); // sync to sharing url
};
$scope.moveColumn = function moveColumn(columnName, newIndex) {
columnActions.moveColumn($scope.panel.columns, columnName, newIndex);
$scope.saveState(); // sync to sharing url
};
}
$scope.filter = function (field, value, operator) {

View file

@ -17,24 +17,32 @@ describe('discoverField', function () {
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, $rootScope, $compile) {
$elem = angular.element('<discover-field></discover-field>');
$elem = angular.element(`
<discover-field
field="field"
on-add-field="addField"
on-remove-field="removeField"
on-show-details="showDetails"
></discover-field>
`);
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
_.assign($rootScope, {
field: indexPattern.fields.byName.extension,
increaseFieldCounter: sinon.spy(),
toggle: function (field) {
indexPattern.fields.byName[field].display = !indexPattern.fields.byName[field].display;
}
addField: sinon.spy(() => $rootScope.field.display = true),
removeField: sinon.spy(() => $rootScope.field.display = false),
showDetails: sinon.spy(() => $rootScope.field.details = { exists: true }),
});
$compile($elem)($rootScope);
$scope = $elem.scope();
$scope = $elem.isolateScope();
$scope.$digest();
sinon.spy($scope, 'toggleDetails');
}));
afterEach(function () {
$scope.toggleDetails.restore();
$scope.$destroy();
});
@ -43,14 +51,27 @@ describe('discoverField', function () {
expect($scope.toggleDisplay).to.be.a(Function);
});
it('should toggle the display of the field', function () {
it('should call onAddField or onRemoveField depending on the display state', function () {
$scope.toggleDisplay($scope.field);
expect($scope.field.display).to.be(true);
expect($scope.onAddField.callCount).to.be(1);
expect($scope.onAddField.firstCall.args).to.eql([$scope.field.name]);
$scope.toggleDisplay($scope.field);
expect($scope.onRemoveField.callCount).to.be(1);
expect($scope.onRemoveField.firstCall.args).to.eql([$scope.field.name]);
});
it('should increase the field popularity', function () {
it('should call toggleDetails when currently showing the details', function () {
$scope.toggleDetails($scope.field);
$scope.toggleDisplay($scope.field);
expect($scope.increaseFieldCounter.called).to.be(true);
expect($scope.toggleDetails.callCount).to.be(2);
});
});
describe('toggleDetails', function () {
it('should notify the parent when showing the details', function () {
$scope.toggleDetails($scope.field);
expect($scope.onShowDetails.callCount).to.be(1);
});
});
});

View file

@ -37,18 +37,20 @@ const destroy = function () {
};
describe('discover field chooser directives', function () {
const $elem = angular.element(
'<disc-field-chooser' +
' columns="columns"' +
' toggle="toggle"' +
' hits="hits"' +
' field-counts="fieldCounts"' +
' filter="filter"' +
' index-pattern="indexPattern"' +
' index-pattern-list="indexPatternList"' +
' state="state">' +
'</disc-field-chooser>'
);
const $elem = angular.element(`
<disc-field-chooser
columns="columns"
toggle="toggle"
hits="hits"
field-counts="fieldCounts"
index-pattern="indexPattern"
index-pattern-list="indexPatternList"
state="state"
on-add-field="addField"
on-add-filter="addFilter"
on-remove-field="removeField"
></disc-field-chooser>
`);
beforeEach(ngMock.module('kibana', ($provide) => {
$provide.decorator('config', ($delegate) => {
@ -81,9 +83,11 @@ describe('discover field chooser directives', function () {
toggle: sinon.spy(),
hits: hits,
fieldCounts: fieldCounts,
filter: sinon.spy(),
addField: sinon.spy(),
addFilter: sinon.spy(),
indexPattern: indexPattern,
indexPatternList: indexPatternList
indexPatternList: indexPatternList,
removeField: sinon.spy(),
});
$scope.$digest();
@ -195,37 +199,37 @@ describe('discover field chooser directives', function () {
field = getField();
});
it('should have a details function', function () {
expect($scope.details).to.be.a(Function);
it('should have a computeDetails function', function () {
expect($scope.computeDetails).to.be.a(Function);
});
it('should increase the field popularity when called', function () {
indexPattern.popularizeField = sinon.spy();
$scope.details(field);
$scope.computeDetails(field);
expect(indexPattern.popularizeField.called).to.be(true);
});
it('should append a details object to the field', function () {
$scope.details(field);
$scope.computeDetails(field);
expect(field.details).to.not.be(undefined);
});
it('should delete the field details if they already exist', function () {
$scope.details(field);
$scope.computeDetails(field);
expect(field.details).to.not.be(undefined);
$scope.details(field);
$scope.computeDetails(field);
expect(field.details).to.be(undefined);
});
it('... unless recompute is true', function () {
$scope.details(field);
$scope.computeDetails(field);
expect(field.details).to.not.be(undefined);
$scope.details(field, true);
$scope.computeDetails(field, true);
expect(field.details).to.not.be(undefined);
});
it('should create buckets with formatted and raw values', function () {
$scope.details(field);
$scope.computeDetails(field);
expect(field.details.buckets).to.not.be(undefined);
expect(field.details.buckets[0].value).to.be(40.141592);
expect(field.details.buckets[0].display).to.be('40.142');
@ -239,7 +243,7 @@ describe('discover field chooser directives', function () {
$scope.$apply();
field = getField();
$scope.details(field);
$scope.computeDetails(field);
expect(getField().details.total).to.be(1);
$scope.hits = [

View file

@ -14,9 +14,16 @@ app.directive('discoverField', function ($compile) {
restrict: 'E',
template: html,
replace: true,
scope: {
field: '=',
onAddField: '=',
onAddFilter: '=',
onRemoveField: '=',
onShowDetails: '=',
},
link: function ($scope, $elem) {
let detailsElem;
let detailScope = $scope.$new();
let detailScope;
const init = function () {
@ -60,9 +67,11 @@ app.directive('discoverField', function ($compile) {
};
$scope.toggleDisplay = function (field) {
// This is inherited from fieldChooser
$scope.toggle(field.name);
if (field.display) $scope.increaseFieldCounter(field);
if (field.display) {
$scope.onRemoveField(field.name);
} else {
$scope.onAddField(field.name);
}
if (field.details) {
$scope.toggleDetails(field);
@ -71,9 +80,7 @@ app.directive('discoverField', function ($compile) {
$scope.toggleDetails = function (field, recompute) {
if (_.isUndefined(field.details) || recompute) {
// This is inherited from fieldChooser
$scope.details(field, recompute);
detailScope.$destroy();
$scope.onShowDetails(field, recompute);
detailScope = $scope.$new();
detailScope.warnings = getWarnings(field);
@ -82,6 +89,7 @@ app.directive('discoverField', function ($compile) {
$elem.append(detailsElem).addClass('active');
} else {
delete field.details;
detailScope.$destroy();
detailsElem.remove();
$elem.removeClass('active');
}

View file

@ -24,7 +24,14 @@
<h5>Selected Fields</h5>
</div>
<ul class="list-unstyled discover-selected-fields" >
<discover-field ng-repeat="field in fields.raw|filter:{display:true}">
<discover-field
ng-repeat="field in fields.raw|filter:{display:true}"
field="field"
on-add-field="onAddField"
on-add-filter="onAddFilter"
on-remove-field="onRemoveField"
on-show-details="computeDetails"
>
</discover-field>
</ul>
@ -108,7 +115,13 @@
<h6>Popular</h6>
</li>
<discover-field
ng-repeat="field in popularFields | filter:filter.isFieldFiltered">
ng-repeat="field in popularFields | filter:filter.isFieldFiltered"
field="field"
on-add-field="onAddField"
on-add-filter="onAddFilter"
on-remove-field="onRemoveField"
on-show-details="computeDetails"
>
</discover-field>
</ul>
@ -116,7 +129,13 @@
ng-class="{ 'hidden-sm': !showFields, 'hidden-xs': !showFields }"
class="list-unstyled discover-unpopular-fields">
<discover-field
ng-repeat="field in unpopularFields | filter:filter.isFieldFiltered">
ng-repeat="field in unpopularFields | filter:filter.isFieldFiltered"
field="field"
on-add-field="onAddField"
on-add-filter="onAddFilter"
on-remove-field="onRemoveField"
on-show-details="computeDetails"
>
</discover-field>
</ul>

View file

@ -26,7 +26,9 @@ app.directive('discFieldChooser', function ($location, globalState, config, $rou
state: '=',
indexPattern: '=',
indexPatternList: '=',
updateFilterInQuery: '=filter'
onAddField: '=',
onAddFilter: '=',
onRemoveField: '=',
},
template: fieldChooserTemplate,
link: function ($scope) {
@ -98,11 +100,6 @@ app.directive('discFieldChooser', function ($location, globalState, config, $rou
filter.active = filter.getActive();
});
$scope.toggle = function (fieldName) {
$scope.increaseFieldCounter(fieldName);
_.toggleInOut($scope.columns, fieldName);
};
$scope.$watchMulti([
'[]fieldCounts',
'[]columns',
@ -156,7 +153,7 @@ app.directive('discFieldChooser', function ($location, globalState, config, $rou
$scope.indexPattern.popularizeField(fieldName, 1);
};
$scope.vizLocation = function (field) {
function getVisualizeUrl(field) {
if (!$scope.state) {return '';}
let agg = {};
@ -210,16 +207,21 @@ app.directive('discFieldChooser', function ($location, globalState, config, $rou
}
})
}));
};
}
$scope.details = function (field, recompute) {
$scope.computeDetails = function (field, recompute) {
if (_.isUndefined(field.details) || recompute) {
field.details = fieldCalculator.getFieldValueCounts({
hits: $scope.hits,
field: field,
count: 5,
grouped: false
});
field.details = Object.assign(
{
visualizeUrl: field.visualizable ? getVisualizeUrl(field) : null,
},
fieldCalculator.getFieldValueCounts({
hits: $scope.hits,
field: field,
count: 5,
grouped: false
}),
);
_.each(field.details.buckets, function (bucket) {
bucket.display = field.format.convert(bucket.value);
});

View file

@ -5,7 +5,7 @@
(
<a
ng-show="!indexPattern.metaFields.includes(field.name)"
ng-click="updateFilterInQuery('_exists_', field.name, '+')">
ng-click="onAddFilter('_exists_', field.name, '+')">
{{::field.details.exists}}
</a>
<span
@ -26,9 +26,9 @@
<div>
<span ng-show="field.filterable" class="pull-right">
<i aria-hidden="true" class="fa fa-search-minus pull-right discover-field-details-filter"
ng-click="updateFilterInQuery(field, bucket.value, '-')" data-test-subj="minus-{{::field.name}}-{{::bucket.display}}"></i>
ng-click="onAddFilter(field, bucket.value, '-')" data-test-subj="minus-{{::field.name}}-{{::bucket.display}}"></i>
<i aria-hidden="true" class="fa fa-search-plus pull-right discover-field-details-filter"
ng-click="updateFilterInQuery(field, bucket.value, '+')" data-test-subj="plus-{{::field.name}}-{{::bucket.display}}"></i>
ng-click="onAddFilter(field, bucket.value, '+')" data-test-subj="plus-{{::field.name}}-{{::bucket.display}}"></i>
</span>
<div css-truncate css-truncate-expandable="true" class="discover-field-details-value" title="{{::bucket.display}}">
{{::bucket.display}} <i ng-show="bucket.display === ''">Empty string</i>
@ -43,7 +43,7 @@
</div>
<a
ng-href="{{vizLocation(field)}}"
ng-href="{{field.details.visualizeUrl}}"
ng-show="field.visualizable"
class="sidebar-item-button primary"
data-test-subj="fieldVisualize-{{::field.name}}">

View file

@ -2,6 +2,7 @@ import _ from 'lodash';
import angular from 'angular';
import moment from 'moment';
import getSort from 'ui/doc_table/lib/get_sort';
import * as columnActions from 'ui/doc_table/actions/columns';
import dateMath from '@elastic/datemath';
import 'ui/doc_table';
import 'ui/visualize';
@ -485,6 +486,20 @@ function discoverController($scope, config, courier, $route, $window, Notifier,
filterManager.add(field, values, operation, $state.index);
};
$scope.addColumn = function addColumn(columnName) {
$scope.indexPattern.popularizeField(columnName, 1);
columnActions.addColumn($scope.state.columns, columnName);
};
$scope.removeColumn = function removeColumn(columnName) {
$scope.indexPattern.popularizeField(columnName, 1);
columnActions.removeColumn($scope.state.columns, columnName);
};
$scope.moveColumn = function moveColumn(columnName, newIndex) {
columnActions.moveColumn($scope.state.columns, columnName, newIndex);
};
$scope.toTop = function () {
$window.scrollTo(0, 0);
};

View file

@ -61,10 +61,13 @@
columns="state.columns"
hits="rows"
field-counts="fieldCounts"
filter="filterQuery"
index-pattern="searchSource.get('index')"
index-pattern-list="opts.indexPatternList"
state="state">
state="state"
on-add-field="addColumn"
on-add-filter="filterQuery"
on-remove-field="removeColumn"
>
</disc-field-chooser>
</div>
@ -132,7 +135,10 @@
data-title="{{opts.savedSearch.lastSavedTitle}}"
data-description="{{opts.savedSearch.description}}"
render-counter
on-add-column="addColumn"
on-change-sort-order="setSortOrder"
on-move-column="moveColumn"
on-remove-column="removeColumn"
></doc-table>
<div ng-if="rows.length == opts.sampleSize" class="discover-table-footer">

View file

@ -100,6 +100,8 @@ describe('Doc Table', function () {
index-pattern="indexPattern"
sort-order="sortOrder"
on-change-sort-order="onChangeSortOrder"
on-move-column="moveColumn"
on-remove-column="removeColumn"
></thead>
`);
@ -108,6 +110,8 @@ describe('Doc Table', function () {
columns: [],
sortOrder: [],
onChangeSortOrder: sinon.stub(),
moveColumn: sinon.spy(),
removeColumn: sinon.spy(),
});
});
@ -199,34 +203,25 @@ describe('Doc Table', function () {
});
it('should move columns to the right', function () {
$scope.moveRight('bytes');
expect($scope.columns[1]).to.be('bytes');
$scope.moveRight('bytes');
expect($scope.columns[2]).to.be('bytes');
$scope.moveColumnRight('bytes');
expect($scope.onMoveColumn.callCount).to.be(1);
expect($scope.onMoveColumn.firstCall.args).to.eql(['bytes', 1]);
});
it('shouldnt move the last column to the right', function () {
expect($scope.columns[3]).to.be('point');
$scope.moveRight('point');
expect($scope.columns[3]).to.be('point');
$scope.moveColumnRight('point');
expect($scope.onMoveColumn.callCount).to.be(0);
});
it('should move columns to the left', function () {
$scope.moveLeft('@timestamp');
expect($scope.columns[1]).to.be('@timestamp');
$scope.moveLeft('request_body');
expect($scope.columns[1]).to.be('request_body');
$scope.moveColumnLeft('@timestamp');
expect($scope.onMoveColumn.callCount).to.be(1);
expect($scope.onMoveColumn.firstCall.args).to.eql(['@timestamp', 1]);
});
it('shouldnt move the first column to the left', function () {
expect($scope.columns[0]).to.be('bytes');
$scope.moveLeft('bytes');
expect($scope.columns[0]).to.be('bytes');
$scope.moveColumnLeft('bytes');
expect($scope.onMoveColumn.callCount).to.be(0);
});
});
});

View file

@ -0,0 +1,32 @@
export function addColumn(columns, columnName) {
if (columns.includes(columnName)) {
return;
}
columns.push(columnName);
}
export function removeColumn(columns, columnName) {
if (!columns.includes(columnName)) {
return;
}
columns.splice(columns.indexOf(columnName), 1);
}
export function moveColumn(columns, columnName, newIndex) {
if (newIndex < 0) {
return;
}
if (newIndex >= columns.length) {
return;
}
if (!columns.includes(columnName)) {
return;
}
columns.splice(columns.indexOf(columnName), 1); // remove at old index
columns.splice(newIndex, 0, columnName); // insert before new index
}

View file

@ -8,9 +8,27 @@
{{name | shortDots}} <i ng-class="headerClass(name)" ng-click="cycleSortOrder(name)" tooltip="{{tooltip(name)}}" tooltip-append-to-body="1"></i>
</span>
<span class="table-header-move">
<i ng-click="toggleColumn(name)" ng-show="canRemove(name)" class="fa fa-remove" tooltip="Remove column" tooltip-append-to-body="1"></i>
<i ng-click="moveLeft(name)" class="fa fa-angle-double-left" ng-show="!$first" tooltip="Move column to the left" tooltip-append-to-body="1"></i>
<i ng-click="moveRight(name)" class="fa fa-angle-double-right" ng-show="!$last" tooltip="Move column to the right" tooltip-append-to-body="1"></i>
<i
class="fa fa-remove"
ng-click="onRemoveColumn(name)"
ng-if="canRemoveColumn(name)"
tooltip-append-to-body="1"
tooltip="Remove column"
></i>
<i
class="fa fa-angle-double-left"
ng-click="moveColumnLeft(name)"
ng-if="canMoveColumnLeft(name)"
tooltip-append-to-body="1"
tooltip="Move column to the left"
></i>
<i
class="fa fa-angle-double-right"
ng-click="moveColumnRight(name)"
ng-if="canMoveColumnRight(name)"
tooltip-append-to-body="1"
tooltip="Move column to the right"
></i>
</span>
</th>
</tr>

View file

@ -13,6 +13,8 @@ module.directive('kbnTableHeader', function (shortDotsFilter) {
sortOrder: '=',
indexPattern: '=',
onChangeSortOrder: '=?',
onRemoveColumn: '=?',
onMoveColumn: '=?',
},
template: headerHtml,
controller: function ($scope) {
@ -29,8 +31,25 @@ module.directive('kbnTableHeader', function (shortDotsFilter) {
return 'Sort by ' + shortDotsFilter(column);
};
$scope.canRemove = function (name) {
return (name !== '_source' || $scope.columns.length !== 1);
$scope.canMoveColumnLeft = function canMoveColumn(columnName) {
return (
_.isFunction($scope.onMoveColumn)
&& $scope.columns.indexOf(columnName) > 0
);
};
$scope.canMoveColumnRight = function canMoveColumn(columnName) {
return (
_.isFunction($scope.onMoveColumn)
&& $scope.columns.indexOf(columnName) < $scope.columns.length - 1
);
};
$scope.canRemoveColumn = function canRemoveColumn(columnName) {
return (
_.isFunction($scope.onRemoveColumn)
&& (columnName !== '_source' || $scope.columns.length > 1)
);
};
$scope.headerClass = function (column) {
@ -43,22 +62,24 @@ module.directive('kbnTableHeader', function (shortDotsFilter) {
return ['fa', sortOrder[1] === 'asc' ? 'fa-sort-up' : 'fa-sort-down'];
};
$scope.moveLeft = function (column) {
let index = _.indexOf($scope.columns, column);
if (index === 0) return;
$scope.moveColumnLeft = function moveLeft(columnName) {
const newIndex = $scope.columns.indexOf(columnName) - 1;
_.move($scope.columns, index, --index);
if (newIndex < 0) {
return;
}
$scope.onMoveColumn(columnName, newIndex);
};
$scope.moveRight = function (column) {
let index = _.indexOf($scope.columns, column);
if (index === $scope.columns.length - 1) return;
$scope.moveColumnRight = function moveRight(columnName) {
const newIndex = $scope.columns.indexOf(columnName) + 1;
_.move($scope.columns, index, ++index);
};
if (newIndex >= $scope.columns.length) {
return;
}
$scope.toggleColumn = function (fieldName) {
_.toggleInOut($scope.columns, fieldName);
$scope.onMoveColumn(columnName, newIndex);
};
$scope.cycleSortOrder = function cycleSortOrder(columnName) {

View file

@ -36,7 +36,9 @@ module.directive('kbnTableRow', function ($compile, $httpParamSerializer, kbnUrl
columns: '=',
filter: '=',
indexPattern: '=',
row: '=kbnTableRow'
row: '=kbnTableRow',
onAddColumn: '=?',
onRemoveColumn: '=?',
},
link: function ($scope, $el) {
$el.after('<tr>');

View file

@ -16,5 +16,12 @@
View single document
</a>
</div>
<doc-viewer hit="row" filter="filter" columns="columns" index-pattern="indexPattern"></doc-viewer>
<doc-viewer
columns="columns"
filter="filter"
hit="row"
index-pattern="indexPattern"
on-add-column="onAddColumn"
on-remove-column="onRemoveColumn"
></doc-viewer>
</td>

View file

@ -10,6 +10,8 @@
index-pattern="indexPattern"
sort-order="sorting"
on-change-sort-order="onChangeSortOrder"
on-move-column="onMoveColumn"
on-remove-column="onRemoveColumn"
></thead>
<tbody>
<tr ng-repeat="row in page|limitTo:limit track by row._index+row._type+row._id+row._score+row._version"
@ -19,7 +21,9 @@
index-pattern="indexPattern"
filter="filter"
class="discover-table-row"
on-add-column="onAddColumn"
on-change-sort-order="onChangeSortOrder"
on-remove-column="onRemoveColumn"
></tr>
</tbody>
</table>
@ -32,6 +36,8 @@
index-pattern="indexPattern"
sort-order="sorting"
on-change-sort-order="onChangeSortOrder"
on-move-column="onMoveColumn"
on-remove-column="onRemoveColumn"
></thead>
<tbody>
<tr ng-repeat="row in hits|limitTo:limit track by row._index+row._type+row._id+row._score+row._version"
@ -43,7 +49,9 @@
class="discover-table-row"
ng-class="{'discover-table-row--highlight': row['$$_isAnchor']}"
data-test-subj="docTableRow{{ row['$$_isAnchor'] ? ' docTableAnchorRow' : ''}}"
on-add-column="onAddColumn"
on-change-sort-order="onChangeSortOrder"
on-remove-column="onRemoveColumn"
></tr>
</tbody>
</table>

View file

@ -23,7 +23,10 @@ uiModules.get('kibana')
searchSource: '=?',
infiniteScroll: '=?',
filter: '=?',
onAddColumn: '=?',
onChangeSortOrder: '=?',
onMoveColumn: '=?',
onRemoveColumn: '=?',
},
link: function ($scope) {
const notify = new Notifier();

View file

@ -14,7 +14,9 @@ uiModules.get('kibana')
hit: '=',
indexPattern: '=',
filter: '=?',
columns: '=?'
columns: '=?',
onAddColumn: '=?',
onRemoveColumn: '=?',
},
template: function ($el) {
const $viewer = $('<div class="doc-viewer">');
@ -28,7 +30,14 @@ uiModules.get('kibana')
<a ng-click="mode='${view.name}'">${view.title}</a>
</li>`);
$tabs.append($tab);
const $viewAttrs = 'hit="hit" index-pattern="indexPattern" filter="filter" columns="columns"';
const $viewAttrs = `
hit="hit"
index-pattern="indexPattern"
filter="filter"
columns="columns"
on-add-column="onAddColumn"
on-remove-column="onRemoveColumn"
`;
const $ext = $(`<render-directive ${$viewAttrs} ng-if="mode == '${view.name}'" definition="docViews['${view.name}'].directive">
</render-directive>`);
$ext.html(view.directive.template);

View file

@ -261,8 +261,7 @@ export default class DiscoverPage {
}
clickFieldListItem(field) {
return this.findTimeout
.findByCssSelector('li[attr-field="' + field + '"]').click();
return PageObjects.common.clickTestSubject(`field-${field}`);
}
async clickFieldListItemAdd(field) {
@ -296,6 +295,7 @@ export default class DiscoverPage {
async removeAllFilters() {
await PageObjects.common.clickTestSubject('showFilterActions');
await PageObjects.common.clickTestSubject('removeAllFilters');
await PageObjects.header.waitUntilLoadingHasFinished();
}