')
.attr('ng-controller', 'KbnTableVisController')
@@ -157,7 +162,7 @@ describe('Table Vis - Controller', async function () {
it('sets the sort on the scope when it is passed as a vis param', async function () {
const sortObj = {
columnIndex: 1,
- direction: 'asc'
+ direction: 'asc',
};
const vis = new OneRangeVis({ sort: sortObj });
initController(vis);
@@ -181,7 +186,6 @@ describe('Table Vis - Controller', async function () {
});
it('passes partialRows:true to tabify based on the vis params', function () {
-
const vis = new OneRangeVis({ showPartialRows: true });
initController(vis);
@@ -189,7 +193,6 @@ describe('Table Vis - Controller', async function () {
});
it('passes partialRows:false to tabify based on the vis params', function () {
-
const vis = new OneRangeVis({ showPartialRows: false });
initController(vis);
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js
index b0476ae224866..8521ee729f313 100644
--- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js
+++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js
@@ -31,10 +31,9 @@ import { round } from 'lodash';
import { VisFactoryProvider } from 'ui/vis/vis_factory';
import { createTableVisTypeDefinition } from '../../table_vis_type';
-import { visualizations } from '../../../../visualizations/public';
+import { setup } from '../../../../visualizations/public/np_ready/public/legacy';
describe('Table Vis - AggTable Directive', function () {
-
let $rootScope;
let $compile;
let Vis;
@@ -51,19 +50,21 @@ describe('Table Vis - AggTable Directive', function () {
const vis2 = new Vis(indexPattern, {
type: 'table',
params: {
- showMetricsAtAllLevels: true
+ showMetricsAtAllLevels: true,
},
aggs: [
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } },
{ type: 'terms', schema: 'bucket', params: { field: 'extension' } },
{ type: 'terms', schema: 'bucket', params: { field: 'geo.src' } },
- { type: 'terms', schema: 'bucket', params: { field: 'machine.os' } }
- ]
+ { type: 'terms', schema: 'bucket', params: { field: 'machine.os' } },
+ ],
});
vis2.aggs.aggs.forEach(function (agg, i) {
agg.id = 'agg_' + (i + 1);
});
- tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, fixtures.threeTermBuckets, { metricsAtAllLevels: true });
+ tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, fixtures.threeTermBuckets, {
+ metricsAtAllLevels: true,
+ });
const vis3 = new Vis(indexPattern, {
type: 'table',
@@ -71,41 +72,54 @@ describe('Table Vis - AggTable Directive', function () {
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } },
{ type: 'min', schema: 'metric', params: { field: '@timestamp' } },
{ type: 'terms', schema: 'bucket', params: { field: 'extension' } },
- { type: 'date_histogram', schema: 'bucket', params: { field: '@timestamp', interval: 'd' } },
- { type: 'derivative', schema: 'metric',
- params: { metricAgg: 'custom', customMetric: { id: '5-orderAgg', type: 'count' } } },
- { type: 'top_hits', schema: 'metric', params: { field: 'bytes', aggregate: { val: 'min' }, size: 1 } }
- ]
+ {
+ type: 'date_histogram',
+ schema: 'bucket',
+ params: { field: '@timestamp', interval: 'd' },
+ },
+ {
+ type: 'derivative',
+ schema: 'metric',
+ params: { metricAgg: 'custom', customMetric: { id: '5-orderAgg', type: 'count' } },
+ },
+ {
+ type: 'top_hits',
+ schema: 'metric',
+ params: { field: 'bytes', aggregate: { val: 'min' }, size: 1 },
+ },
+ ],
});
vis3.aggs.aggs.forEach(function (agg, i) {
agg.id = 'agg_' + (i + 1);
});
- tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative =
- tabifyAggResponse(vis3.aggs, fixtures.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative);
+ tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative = tabifyAggResponse(
+ vis3.aggs,
+ fixtures.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative
+ );
};
beforeEach(ngMock.module('kibana'));
- beforeEach(ngMock.inject(function ($injector, Private, config) {
- legacyDependencies = {
- // eslint-disable-next-line new-cap
- createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization
- };
+ beforeEach(
+ ngMock.inject(function ($injector, Private, config) {
+ legacyDependencies = {
+ // eslint-disable-next-line new-cap
+ createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization,
+ };
- visualizations.types.VisTypesRegistryProvider.register(() =>
- createTableVisTypeDefinition(legacyDependencies)
- );
+ setup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies));
- tableAggResponse = legacyResponseHandlerProvider().handler;
- indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
- Vis = Private(VisProvider);
- settings = config;
+ tableAggResponse = legacyResponseHandlerProvider().handler;
+ indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
+ Vis = Private(VisProvider);
+ settings = config;
- $rootScope = $injector.get('$rootScope');
- $compile = $injector.get('$compile');
+ $rootScope = $injector.get('$rootScope');
+ $compile = $injector.get('$compile');
- init();
- }));
+ init();
+ })
+ );
let $scope;
beforeEach(function () {
@@ -115,12 +129,16 @@ describe('Table Vis - AggTable Directive', function () {
$scope.$destroy();
});
-
it('renders a simple response properly', async function () {
- $scope.dimensions = { metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }], buckets: [] };
+ $scope.dimensions = {
+ metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }],
+ buckets: [],
+ };
$scope.table = (await tableAggResponse(tabifiedData.metricOnly, $scope.dimensions)).tables[0];
- const $el = $compile('
')($scope);
+ const $el = $compile('
')(
+ $scope
+ );
$scope.$digest();
expect($el.find('tbody').length).to.be(1);
@@ -131,7 +149,9 @@ describe('Table Vis - AggTable Directive', function () {
it('renders nothing if the table is empty', function () {
$scope.dimensions = {};
$scope.table = null;
- const $el = $compile('
')($scope);
+ const $el = $compile('
')(
+ $scope
+ );
$scope.$digest();
expect($el.find('tbody').length).to.be(0);
@@ -139,10 +159,21 @@ describe('Table Vis - AggTable Directive', function () {
it('renders a complex response properly', async function () {
$scope.dimensions = {
- buckets: [{ accessor: 0, params: {} }, { accessor: 2, params: {} }, { accessor: 4, params: {} }],
- metrics: [{ accessor: 1, params: {} }, { accessor: 3, params: {} }, { accessor: 5, params: {} }]
+ buckets: [
+ { accessor: 0, params: {} },
+ { accessor: 2, params: {} },
+ { accessor: 4, params: {} },
+ ],
+ metrics: [
+ { accessor: 1, params: {} },
+ { accessor: 3, params: {} },
+ { accessor: 5, params: {} },
+ ],
};
- $scope.table = (await tableAggResponse(tabifiedData.threeTermBuckets, $scope.dimensions)).tables[0];
+ $scope.table = (await tableAggResponse(
+ tabifiedData.threeTermBuckets,
+ $scope.dimensions
+ )).tables[0];
const $el = $('
');
$compile($el)($scope);
$scope.$digest();
@@ -165,7 +196,9 @@ describe('Table Vis - AggTable Directive', function () {
expect($cells.length).to.be(6);
const txts = $cells.map(function () {
- return $(this).text().trim();
+ return $(this)
+ .text()
+ .trim();
});
// two character country code
@@ -184,7 +217,6 @@ describe('Table Vis - AggTable Directive', function () {
describe('renders totals row', function () {
async function totalsRowTest(totalFunc, expected) {
-
function setDefaultTimezone() {
moment.tz.setDefault(settings.get('dateFormat:tz'));
}
@@ -197,15 +229,18 @@ describe('Table Vis - AggTable Directive', function () {
buckets: [
{ accessor: 0, params: {} },
{ accessor: 1, format: { id: 'date', params: { pattern: 'YYYY-MM-DD' } } },
- ], metrics: [
+ ],
+ metrics: [
{ accessor: 2, format: { id: 'number' } },
{ accessor: 3, format: { id: 'date' } },
{ accessor: 4, format: { id: 'number' } },
{ accessor: 5, format: { id: 'number' } },
- ]
+ ],
};
const response = await tableAggResponse(
- tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative, $scope.dimensions);
+ tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative,
+ $scope.dimensions
+ );
$scope.table = response.tables[0];
$scope.showTotal = true;
$scope.totalFunc = totalFunc;
@@ -226,7 +261,11 @@ describe('Table Vis - AggTable Directive', function () {
expect($cells.length).to.be(6);
for (let i = 0; i < 6; i++) {
- expect($($cells[i]).text().trim()).to.be(expected[i]);
+ expect(
+ $($cells[i])
+ .text()
+ .trim()
+ ).to.be(expected[i]);
}
settings.set('dateFormat:tz', oldTimezoneSetting);
off();
@@ -241,7 +280,7 @@ describe('Table Vis - AggTable Directive', function () {
'9,283',
'Sep 28, 2014 @ 00:00:00.000',
'1',
- '11'
+ '11',
]);
});
it('as max', async function () {
@@ -251,34 +290,22 @@ describe('Table Vis - AggTable Directive', function () {
'220,943',
'Oct 3, 2014 @ 00:00:00.000',
'239',
- '837'
+ '837',
]);
});
it('as avg', async function () {
- await totalsRowTest('avg', [
- '',
- '',
- '87,221.5',
- '',
- '64.667',
- '206.833'
- ]);
+ await totalsRowTest('avg', ['', '', '87,221.5', '', '64.667', '206.833']);
});
it('as sum', async function () {
- await totalsRowTest('sum', [
- '',
- '',
- '1,569,987',
- '',
- '1,164',
- '3,723'
- ]);
+ await totalsRowTest('sum', ['', '', '1,569,987', '', '1,164', '3,723']);
});
});
describe('aggTable.toCsv()', function () {
it('escapes rows and columns properly', function () {
- const $el = $compile('
')($scope);
+ const $el = $compile('
')(
+ $scope
+ );
$scope.$digest();
const $tableScope = $el.isolateScope();
@@ -289,25 +316,35 @@ describe('Table Vis - AggTable Directive', function () {
{ id: 'b', name: 'two' },
{ id: 'c', name: 'with double-quotes(")' },
],
- rows: [
- { a: 1, b: 2, c: '"foobar"' },
- ],
+ rows: [{ a: 1, b: 2, c: '"foobar"' }],
};
expect(aggTable.toCsv()).to.be(
- 'one,two,"with double-quotes("")"' + '\r\n' +
- '1,2,"""foobar"""' + '\r\n'
+ 'one,two,"with double-quotes("")"' + '\r\n' + '1,2,"""foobar"""' + '\r\n'
);
});
it('exports rows and columns properly', async function () {
$scope.dimensions = {
- buckets: [{ accessor: 0, params: {} }, { accessor: 2, params: {} }, { accessor: 4, params: {} }],
- metrics: [{ accessor: 1, params: {} }, { accessor: 3, params: {} }, { accessor: 5, params: {} }]
+ buckets: [
+ { accessor: 0, params: {} },
+ { accessor: 2, params: {} },
+ { accessor: 4, params: {} },
+ ],
+ metrics: [
+ { accessor: 1, params: {} },
+ { accessor: 3, params: {} },
+ { accessor: 5, params: {} },
+ ],
};
- $scope.table = (await tableAggResponse(tabifiedData.threeTermBuckets, $scope.dimensions)).tables[0];
+ $scope.table = (await tableAggResponse(
+ tabifiedData.threeTermBuckets,
+ $scope.dimensions
+ )).tables[0];
- const $el = $compile('
')($scope);
+ const $el = $compile('
')(
+ $scope
+ );
$scope.$digest();
const $tableScope = $el.isolateScope();
@@ -316,30 +353,56 @@ describe('Table Vis - AggTable Directive', function () {
const raw = aggTable.toCsv(false);
expect(raw).to.be(
- '"extension: Descending","Average bytes","geo.src: Descending","Average bytes","machine.os: Descending","Average bytes"' + '\r\n' +
- 'png,IT,win,412032,9299,0' + '\r\n' +
- 'png,IT,mac,412032,9299,9299' + '\r\n' +
- 'png,US,linux,412032,8293,3992' + '\r\n' +
- 'png,US,mac,412032,8293,3029' + '\r\n' +
- 'css,MX,win,412032,9299,4992' + '\r\n' +
- 'css,MX,mac,412032,9299,5892' + '\r\n' +
- 'css,US,linux,412032,8293,3992' + '\r\n' +
- 'css,US,mac,412032,8293,3029' + '\r\n' +
- 'html,CN,win,412032,9299,4992' + '\r\n' +
- 'html,CN,mac,412032,9299,5892' + '\r\n' +
- 'html,FR,win,412032,8293,3992' + '\r\n' +
- 'html,FR,mac,412032,8293,3029' + '\r\n'
+ '"extension: Descending","Average bytes","geo.src: Descending","Average bytes","machine.os: Descending","Average bytes"' +
+ '\r\n' +
+ 'png,IT,win,412032,9299,0' +
+ '\r\n' +
+ 'png,IT,mac,412032,9299,9299' +
+ '\r\n' +
+ 'png,US,linux,412032,8293,3992' +
+ '\r\n' +
+ 'png,US,mac,412032,8293,3029' +
+ '\r\n' +
+ 'css,MX,win,412032,9299,4992' +
+ '\r\n' +
+ 'css,MX,mac,412032,9299,5892' +
+ '\r\n' +
+ 'css,US,linux,412032,8293,3992' +
+ '\r\n' +
+ 'css,US,mac,412032,8293,3029' +
+ '\r\n' +
+ 'html,CN,win,412032,9299,4992' +
+ '\r\n' +
+ 'html,CN,mac,412032,9299,5892' +
+ '\r\n' +
+ 'html,FR,win,412032,8293,3992' +
+ '\r\n' +
+ 'html,FR,mac,412032,8293,3029' +
+ '\r\n'
);
});
it('exports formatted rows and columns properly', async function () {
$scope.dimensions = {
- buckets: [{ accessor: 0, params: {} }, { accessor: 2, params: {} }, { accessor: 4, params: {} }],
- metrics: [{ accessor: 1, params: {} }, { accessor: 3, params: {} }, { accessor: 5, params: {} }]
+ buckets: [
+ { accessor: 0, params: {} },
+ { accessor: 2, params: {} },
+ { accessor: 4, params: {} },
+ ],
+ metrics: [
+ { accessor: 1, params: {} },
+ { accessor: 3, params: {} },
+ { accessor: 5, params: {} },
+ ],
};
- $scope.table = (await tableAggResponse(tabifiedData.threeTermBuckets, $scope.dimensions)).tables[0];
+ $scope.table = (await tableAggResponse(
+ tabifiedData.threeTermBuckets,
+ $scope.dimensions
+ )).tables[0];
- const $el = $compile('
')($scope);
+ const $el = $compile('
')(
+ $scope
+ );
$scope.$digest();
const $tableScope = $el.isolateScope();
@@ -351,19 +414,32 @@ describe('Table Vis - AggTable Directive', function () {
const formatted = aggTable.toCsv(true);
expect(formatted).to.be(
- '"extension: Descending","Average bytes","geo.src: Descending","Average bytes","machine.os: Descending","Average bytes"' + '\r\n' +
- '"png_formatted",IT,win,412032,9299,0' + '\r\n' +
- '"png_formatted",IT,mac,412032,9299,9299' + '\r\n' +
- '"png_formatted",US,linux,412032,8293,3992' + '\r\n' +
- '"png_formatted",US,mac,412032,8293,3029' + '\r\n' +
- '"css_formatted",MX,win,412032,9299,4992' + '\r\n' +
- '"css_formatted",MX,mac,412032,9299,5892' + '\r\n' +
- '"css_formatted",US,linux,412032,8293,3992' + '\r\n' +
- '"css_formatted",US,mac,412032,8293,3029' + '\r\n' +
- '"html_formatted",CN,win,412032,9299,4992' + '\r\n' +
- '"html_formatted",CN,mac,412032,9299,5892' + '\r\n' +
- '"html_formatted",FR,win,412032,8293,3992' + '\r\n' +
- '"html_formatted",FR,mac,412032,8293,3029' + '\r\n'
+ '"extension: Descending","Average bytes","geo.src: Descending","Average bytes","machine.os: Descending","Average bytes"' +
+ '\r\n' +
+ '"png_formatted",IT,win,412032,9299,0' +
+ '\r\n' +
+ '"png_formatted",IT,mac,412032,9299,9299' +
+ '\r\n' +
+ '"png_formatted",US,linux,412032,8293,3992' +
+ '\r\n' +
+ '"png_formatted",US,mac,412032,8293,3029' +
+ '\r\n' +
+ '"css_formatted",MX,win,412032,9299,4992' +
+ '\r\n' +
+ '"css_formatted",MX,mac,412032,9299,5892' +
+ '\r\n' +
+ '"css_formatted",US,linux,412032,8293,3992' +
+ '\r\n' +
+ '"css_formatted",US,mac,412032,8293,3029' +
+ '\r\n' +
+ '"html_formatted",CN,win,412032,9299,4992' +
+ '\r\n' +
+ '"html_formatted",CN,mac,412032,9299,5892' +
+ '\r\n' +
+ '"html_formatted",FR,win,412032,8293,3992' +
+ '\r\n' +
+ '"html_formatted",FR,mac,412032,8293,3029' +
+ '\r\n'
);
});
});
@@ -452,9 +528,7 @@ describe('Table Vis - AggTable Directive', function () {
{ id: 'b', name: 'two' },
{ id: 'c', name: 'with double-quotes(")' },
],
- rows: [
- { a: 1, b: 2, c: '"foobar"' },
- ],
+ rows: [{ a: 1, b: 2, c: '"foobar"' }],
};
aggTable.csv.filename = 'somefilename.csv';
@@ -464,25 +538,26 @@ describe('Table Vis - AggTable Directive', function () {
const call = saveAs.getCall(0);
expect(call.args[0]).to.be.a(FakeBlob);
expect(call.args[0].slices).to.eql([
- 'one,two,"with double-quotes("")"' + '\r\n' +
- '1,2,"""foobar"""' + '\r\n'
+ 'one,two,"with double-quotes("")"' + '\r\n' + '1,2,"""foobar"""' + '\r\n',
]);
expect(call.args[0].opts).to.eql({
- type: 'text/plain;charset=utf-8'
+ type: 'text/plain;charset=utf-8',
});
expect(call.args[1]).to.be('somefilename.csv');
});
it('should use the export-title attribute', function () {
const expected = 'export file name';
- const $el = $compile(`
`)($scope);
+ const $el = $compile(
+ ``
+ )($scope);
$scope.$digest();
const $tableScope = $el.isolateScope();
const aggTable = $tableScope.aggTable;
$tableScope.table = {
columns: [],
- rows: []
+ rows: [],
};
$tableScope.exportTitle = expected;
$scope.$digest();
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js
index 5b9974d6ff4d5..7998a92a4759f 100644
--- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js
+++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js
@@ -28,7 +28,7 @@ import { tabifyAggResponse } from 'ui/agg_response/tabify';
import { VisFactoryProvider } from 'ui/vis/vis_factory';
import { createTableVisTypeDefinition } from '../../table_vis_type';
-import { visualizations } from '../../../../visualizations/public';
+import { setup } from '../../../../visualizations/public/np_ready/public/legacy';
describe('Table Vis - AggTableGroup Directive', function () {
let $rootScope;
@@ -49,8 +49,8 @@ describe('Table Vis - AggTableGroup Directive', function () {
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } },
{ type: 'terms', schema: 'split', params: { field: 'extension' } },
{ type: 'terms', schema: 'segment', params: { field: 'geo.src' } },
- { type: 'terms', schema: 'segment', params: { field: 'machine.os' } }
- ]
+ { type: 'terms', schema: 'segment', params: { field: 'machine.os' } },
+ ],
});
vis2.aggs.aggs.forEach(function (agg, i) {
agg.id = 'agg_' + (i + 1);
@@ -59,25 +59,25 @@ describe('Table Vis - AggTableGroup Directive', function () {
};
beforeEach(ngMock.module('kibana'));
- beforeEach(ngMock.inject(function ($injector, Private) {
- legacyDependencies = {
- // eslint-disable-next-line new-cap
- createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization
- };
+ beforeEach(
+ ngMock.inject(function ($injector, Private) {
+ legacyDependencies = {
+ // eslint-disable-next-line new-cap
+ createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization,
+ };
- visualizations.types.VisTypesRegistryProvider.register(() =>
- createTableVisTypeDefinition(legacyDependencies)
- );
+ setup.types.registerVisualization(() => createTableVisTypeDefinition(legacyDependencies));
- tableAggResponse = legacyResponseHandlerProvider().handler;
- indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
- Vis = Private(VisProvider);
+ tableAggResponse = legacyResponseHandlerProvider().handler;
+ indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
+ Vis = Private(VisProvider);
- $rootScope = $injector.get('$rootScope');
- $compile = $injector.get('$compile');
+ $rootScope = $injector.get('$rootScope');
+ $compile = $injector.get('$compile');
- init();
- }));
+ init();
+ })
+ );
let $scope;
beforeEach(function () {
@@ -88,13 +88,18 @@ describe('Table Vis - AggTableGroup Directive', function () {
});
it('renders a simple split response properly', async function () {
- $scope.dimensions = { metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }], buckets: [] };
+ $scope.dimensions = {
+ metrics: [{ accessor: 0, format: { id: 'number' }, params: {} }],
+ buckets: [],
+ };
$scope.group = await tableAggResponse(tabifiedData.metricOnly, $scope.dimensions);
$scope.sort = {
columnIndex: null,
- direction: null
+ direction: null,
};
- const $el = $('');
+ const $el = $(
+ ''
+ );
$compile($el)($scope);
$scope.$digest();
@@ -104,10 +109,12 @@ describe('Table Vis - AggTableGroup Directive', function () {
});
it('renders nothing if the table list is empty', function () {
- const $el = $('');
+ const $el = $(
+ ''
+ );
$scope.group = {
- tables: []
+ tables: [],
};
$compile($el)($scope);
@@ -121,10 +128,19 @@ describe('Table Vis - AggTableGroup Directive', function () {
$scope.dimensions = {
splitRow: [{ accessor: 0, params: {} }],
buckets: [{ accessor: 2, params: {} }, { accessor: 4, params: {} }],
- metrics: [{ accessor: 1, params: {} }, { accessor: 3, params: {} }, { accessor: 5, params: {} }]
+ metrics: [
+ { accessor: 1, params: {} },
+ { accessor: 3, params: {} },
+ { accessor: 5, params: {} },
+ ],
};
- const group = $scope.group = await tableAggResponse(tabifiedData.threeTermBuckets, $scope.dimensions);
- const $el = $('');
+ const group = ($scope.group = await tableAggResponse(
+ tabifiedData.threeTermBuckets,
+ $scope.dimensions
+ ));
+ const $el = $(
+ ''
+ );
$compile($el)($scope);
$scope.$digest();
diff --git a/src/legacy/core_plugins/vis_type_table/public/legacy.ts b/src/legacy/core_plugins/vis_type_table/public/legacy.ts
index fded5690a362d..8139a70552c48 100644
--- a/src/legacy/core_plugins/vis_type_table/public/legacy.ts
+++ b/src/legacy/core_plugins/vis_type_table/public/legacy.ts
@@ -22,11 +22,11 @@ import { npSetup, npStart } from 'ui/new_platform';
import { plugin } from '.';
import { TablePluginSetupDependencies } from './plugin';
-import { visualizations } from '../../visualizations/public';
+import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy';
import { LegacyDependenciesPlugin } from './shim';
const plugins: Readonly = {
- visualizations,
+ visualizations: setupVisualizations,
data: npSetup.plugins.data,
// Temporary solution
diff --git a/src/legacy/core_plugins/vis_type_table/public/plugin.ts b/src/legacy/core_plugins/vis_type_table/public/plugin.ts
index 21b6e21d6d639..6a39e8079a9fd 100644
--- a/src/legacy/core_plugins/vis_type_table/public/plugin.ts
+++ b/src/legacy/core_plugins/vis_type_table/public/plugin.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { VisualizationsSetup } from '../../visualizations/public';
+import { VisualizationsSetup } from '../../visualizations/public/np_ready/public';
import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public';
import {
@@ -63,7 +63,7 @@ export class TableVisPlugin implements Plugin, void> {
data.expressions.registerFunction(createTableVisFn);
- visualizations.types.VisTypesRegistryProvider.register(() =>
+ visualizations.types.registerVisualization(() =>
createTableVisTypeDefinition(visualizationDependencies)
);
}
diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts
index 01b2d99ce509c..6a5e06b6e6978 100644
--- a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts
+++ b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy.ts
@@ -20,12 +20,12 @@
import { PluginInitializerContext } from 'kibana/public';
import { npSetup, npStart } from 'ui/new_platform';
-import { visualizations } from '../../visualizations/public';
+import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy';
import { TagCloudPluginSetupDependencies } from './plugin';
import { plugin } from '.';
const plugins: Readonly = {
- visualizations,
+ visualizations: setupVisualizations,
data: npSetup.plugins.data,
};
diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts
index e7f633b4af377..e13e9896e3940 100644
--- a/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts
+++ b/src/legacy/core_plugins/vis_type_tagcloud/public/plugin.ts
@@ -19,7 +19,7 @@
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public';
import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public';
-import { VisualizationsSetup } from '../../visualizations/public';
+import { VisualizationsSetup } from '../../visualizations/public/np_ready/public';
import { createTagCloudFn } from './tag_cloud_fn';
import { createTagCloudTypeDefinition } from './tag_cloud_type';
@@ -40,7 +40,7 @@ export class TagCloudPlugin implements Plugin {
public setup(core: CoreSetup, { data, visualizations }: TagCloudPluginSetupDependencies) {
data.expressions.registerFunction(createTagCloudFn);
- visualizations.types.VisTypesRegistryProvider.register(createTagCloudTypeDefinition);
+ visualizations.types.registerVisualization(createTagCloudTypeDefinition);
}
public start(core: CoreStart) {
diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts
index 0b4d90522cc44..421821d93b045 100644
--- a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts
+++ b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts
@@ -23,7 +23,7 @@ import { Status } from 'ui/vis/update_status';
import { Schemas } from 'ui/vis/editors/default/schemas';
import { TagCloudOptions } from './components/tag_cloud_options';
-import { visFactory } from '../../visualizations/public';
+import { visFactory } from '../../visualizations/public/np_ready/public';
// @ts-ignore
import { TagCloudVisualization } from './components/tag_cloud_visualization';
diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js
index 681f4486a02d7..012f144983e98 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js
+++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js
@@ -41,10 +41,10 @@ import vegaMapImage256 from './vega_map_image_256.png';
import { VegaParser } from '../data_model/vega_parser';
import { SearchCache } from '../data_model/search_cache';
-import { visualizations } from '../../../visualizations/public';
+import { setup } from '../../../visualizations/public/np_ready/public/legacy';
import { createVegaTypeDefinition } from '../vega_type';
-const THRESHOLD = 0.10;
+const THRESHOLD = 0.1;
const PIXEL_DIFF = 30;
describe('VegaVisualizations', () => {
@@ -57,22 +57,24 @@ describe('VegaVisualizations', () => {
let vegaVisualizationDependencies;
beforeEach(ngMock.module('kibana'));
- beforeEach(ngMock.inject((Private, $injector) => {
- vegaVisualizationDependencies = {
- es: $injector.get('es'),
- serviceSettings: $injector.get('serviceSettings'),
- uiSettings: $injector.get('config'),
- };
+ beforeEach(
+ ngMock.inject((Private, $injector) => {
+ vegaVisualizationDependencies = {
+ es: $injector.get('es'),
+ serviceSettings: $injector.get('serviceSettings'),
+ uiSettings: $injector.get('config'),
+ };
- visualizations.types.VisTypesRegistryProvider.register(() =>
- createVegaTypeDefinition(vegaVisualizationDependencies)
- );
+ setup.types.registerVisualization(() =>
+ createVegaTypeDefinition(vegaVisualizationDependencies)
+ );
- Vis = Private(visModule.VisProvider);
+ Vis = Private(visModule.VisProvider);
- VegaVisualization = createVegaVisualization(vegaVisualizationDependencies);
- indexPattern = Private(LogstashIndexPatternStubProvider);
- }));
+ VegaVisualization = createVegaVisualization(vegaVisualizationDependencies);
+ indexPattern = Private(LogstashIndexPatternStubProvider);
+ })
+ );
describe('VegaVisualization - basics', () => {
beforeEach(async function () {
@@ -105,15 +107,12 @@ describe('VegaVisualizations', () => {
await vegaVis.render(vegaParser, vis.params, { resize: true });
const mismatchedPixels2 = await compareImage(vegaliteImage256);
expect(mismatchedPixels2).to.be.lessThan(PIXEL_DIFF);
-
} finally {
vegaVis.destroy();
}
-
});
it('should show vega graph', async function () {
-
let vegaVis;
try {
vegaVis = new VegaVisualization(domNode, vis);
@@ -127,20 +126,16 @@ describe('VegaVisualizations', () => {
} finally {
vegaVis.destroy();
}
-
});
it('should show vegatooltip on mouseover over a vega graph', async () => {
-
let vegaVis;
try {
-
vegaVis = new VegaVisualization(domNode, vis);
const vegaParser = new VegaParser(vegaTooltipGraph, new SearchCache());
await vegaParser.parseAsync();
await vegaVis.render(vegaParser, vis.params, { data: true });
-
const $el = $(domNode);
const offset = $el.offset();
@@ -160,27 +155,24 @@ describe('VegaVisualizations', () => {
expect(tooltip).to.be.ok();
expect(tooltip.innerHTML).to.be(
'This is a long title
' +
- '' +
- 'fieldA: | value of fld1 |
' +
- 'fld2: | 42 |
' +
- '
');
+ '' +
+ 'fieldA: | value of fld1 |
' +
+ 'fld2: | 42 |
' +
+ '
'
+ );
vegaVis.destroy();
tooltip = document.getElementById('vega-kibana-tooltip');
expect(tooltip).to.not.be.ok();
-
} finally {
vegaVis.destroy();
}
-
});
it('should show vega blank rectangle on top of a map (vegamap)', async () => {
-
let vegaVis;
try {
-
vegaVis = new VegaVisualization(domNode, vis);
const vegaParser = new VegaParser(vegaMapGraph, new SearchCache());
await vegaParser.parseAsync();
@@ -191,19 +183,17 @@ describe('VegaVisualizations', () => {
await vegaVis.render(vegaParser, vis.params, { data: true });
const mismatchedPixels = await compareImage(vegaMapImage256);
expect(mismatchedPixels).to.be.lessThan(PIXEL_DIFF);
-
} finally {
vegaVis.destroy();
}
-
});
it('should add a small subpixel value to the height of the canvas to avoid getting it set to 0', async () => {
let vegaVis;
try {
-
vegaVis = new VegaVisualization(domNode, vis);
- const vegaParser = new VegaParser(`{
+ const vegaParser = new VegaParser(
+ `{
"$schema": "https://vega.github.io/schema/vega/v3.json",
"marks": [
{
@@ -222,7 +212,9 @@ describe('VegaVisualizations', () => {
}
}
]
- }`, new SearchCache());
+ }`,
+ new SearchCache()
+ );
await vegaParser.parseAsync();
domNode.style.width = '256px';
@@ -240,10 +232,8 @@ describe('VegaVisualizations', () => {
vegaVis.destroy();
}
});
-
});
-
async function compareImage(expectedImageSource) {
const elementList = domNode.querySelectorAll('canvas');
expect(elementList.length).to.equal(1);
@@ -267,5 +257,4 @@ describe('VegaVisualizations', () => {
domNode.innerHTML = '';
document.body.removeChild(domNode);
}
-
});
diff --git a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts
index 5136046b31a97..15cf97beb5717 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/legacy.ts
+++ b/src/legacy/core_plugins/vis_type_vega/public/legacy.ts
@@ -20,13 +20,13 @@
import { PluginInitializerContext } from 'kibana/public';
import { npSetup, npStart } from 'ui/new_platform';
-import { visualizations } from '../../visualizations/public';
+import { setup as setupVisualizations } from '../../visualizations/public/np_ready/public/legacy';
import { VegaPluginSetupDependencies } from './plugin';
import { LegacyDependenciesPlugin } from './shim';
import { plugin } from '.';
const plugins: Readonly = {
- visualizations,
+ visualizations: setupVisualizations,
data: npSetup.plugins.data,
// Temporary solution
diff --git a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts
index 039ef49cb2289..b2a6fade883ca 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/plugin.ts
+++ b/src/legacy/core_plugins/vis_type_vega/public/plugin.ts
@@ -25,7 +25,7 @@ import {
} from '../../../../core/public';
import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim';
import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public';
-import { VisualizationsSetup } from '../../visualizations/public';
+import { VisualizationsSetup } from '../../visualizations/public/np_ready/public';
import { createVegaFn } from './vega_fn';
import { createVegaTypeDefinition } from './vega_type';
@@ -61,7 +61,7 @@ export class VegaPlugin implements Plugin, void> {
data.expressions.registerFunction(() => createVegaFn(visualizationDependencies));
- visualizations.types.VisTypesRegistryProvider.register(() =>
+ visualizations.types.registerVisualization(() =>
createVegaTypeDefinition(visualizationDependencies)
);
}
diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts
index a930e369c3e1c..22a71bd999d54 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts
+++ b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts
@@ -17,7 +17,8 @@
* under the License.
*/
import { Filter } from '@kbn/es-query';
-import { timefilter, TimeRange } from 'ui/timefilter';
+import { timefilter } from 'ui/timefilter';
+import { TimeRange } from 'src/plugins/data/public';
import { Query } from 'src/legacy/core_plugins/data/public';
// @ts-ignore
diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts
index 6ffcd8867ffea..d1f04c794e3c6 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts
+++ b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts
@@ -25,7 +25,7 @@ import { DefaultEditorSize } from 'ui/vis/editor_size';
import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
import vegaEditorTemplate from './vega_editor_template.html';
-import { visFactory } from '../../visualizations/public';
+import { visFactory } from '../../visualizations/public/np_ready/public';
import { VegaVisualizationDependencies } from './plugin';
import { createVegaRequestHandler } from './vega_request_handler';
diff --git a/src/legacy/core_plugins/visualizations/index.ts b/src/legacy/core_plugins/visualizations/index.ts
index bb9ef1588bdc2..3642071667f48 100644
--- a/src/legacy/core_plugins/visualizations/index.ts
+++ b/src/legacy/core_plugins/visualizations/index.ts
@@ -25,7 +25,7 @@ export default function VisualizationsPlugin(kibana: any) {
const config: Legacy.PluginSpecOptions = {
id: 'visualizations',
require: ['data'],
- publicDir: resolve(__dirname, 'public'),
+ publicDir: resolve(__dirname, 'public/np_ready/public'),
config: (Joi: any) => {
return Joi.object({
enabled: Joi.boolean().default(true),
diff --git a/src/legacy/core_plugins/visualizations/public/index.ts b/src/legacy/core_plugins/visualizations/public/index.ts
deleted file mode 100644
index 10fef3112335b..0000000000000
--- a/src/legacy/core_plugins/visualizations/public/index.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { FiltersService, FiltersSetup } from './filters';
-import { TypesService, TypesSetup } from './types';
-
-class VisualizationsPlugin {
- private readonly filters: FiltersService;
- private readonly types: TypesService;
-
- constructor() {
- this.filters = new FiltersService();
- this.types = new TypesService();
- }
-
- public setup() {
- return {
- filters: this.filters.setup(),
- types: this.types.setup(),
- };
- }
-
- public stop() {
- this.filters.stop();
- this.types.stop();
- }
-}
-
-/**
- * We export visualizations here so that users importing from 'plugins/visualizations'
- * will automatically receive the response value of the `setup` contract, mimicking
- * the data that will eventually be injected by the new platform.
- */
-export const visualizations = new VisualizationsPlugin().setup();
-
-/** @public */
-export interface VisualizationsSetup {
- filters: FiltersSetup;
- types: TypesSetup;
-}
-
-/** @public types */
-export {
- Vis,
- visFactory,
- DefaultEditorSize,
- VisParams,
- VisProvider,
- VisState,
- // VisualizationController,
- // VisType,
- VisTypeAlias,
- VisTypesRegistry,
- Status,
-} from './types';
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json b/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json
new file mode 100644
index 0000000000000..8ecf3dfce6e94
--- /dev/null
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/kibana.json
@@ -0,0 +1,9 @@
+{
+ "id": "visualizations",
+ "version": "kibana",
+ "requiredPlugins": [
+ ],
+ "server": false,
+ "ui": true
+ }
+
\ No newline at end of file
diff --git a/src/legacy/core_plugins/visualizations/public/filters/filters_service.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/filters/filters_service.ts
similarity index 87%
rename from src/legacy/core_plugins/visualizations/public/filters/filters_service.ts
rename to src/legacy/core_plugins/visualizations/public/np_ready/public/filters/filters_service.ts
index 60c26d7cbdc1d..51709f365dbbd 100644
--- a/src/legacy/core_plugins/visualizations/public/filters/filters_service.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/filters/filters_service.ts
@@ -17,8 +17,10 @@
* under the License.
*/
-// @ts-ignore
-import { VisFiltersProvider, createFilter } from 'ui/vis/vis_filters';
+interface SetupDependecies {
+ VisFiltersProvider: any;
+ createFilter: any;
+}
/**
* Vis Filters Service
@@ -26,7 +28,7 @@ import { VisFiltersProvider, createFilter } from 'ui/vis/vis_filters';
* @internal
*/
export class FiltersService {
- public setup() {
+ public setup({ VisFiltersProvider, createFilter }: SetupDependecies) {
return {
VisFiltersProvider,
createFilter,
diff --git a/src/legacy/core_plugins/visualizations/public/filters/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/filters/index.ts
similarity index 100%
rename from src/legacy/core_plugins/visualizations/public/filters/index.ts
rename to src/legacy/core_plugins/visualizations/public/np_ready/public/filters/index.ts
diff --git a/src/dev/build/tasks/nodejs/__tests__/node_shasums.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
similarity index 57%
rename from src/dev/build/tasks/nodejs/__tests__/node_shasums.js
rename to src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
index 3e400283dbd80..d38acaa3cf3f2 100644
--- a/src/dev/build/tasks/nodejs/__tests__/node_shasums.js
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
@@ -17,13 +17,29 @@
* under the License.
*/
-import expect from '@kbn/expect';
+import { PluginInitializerContext } from 'src/core/public';
+import { VisualizationsPublicPlugin, Setup } from './plugin';
-import { getNodeShasums } from '../node_shasums';
+/** @public */
+export type VisualizationsSetup = Setup;
-describe('src/dev/build/tasks/nodejs/node_shasums', () => {
- it('resolves to an object with shasums for node downloads for version', async () => {
- const shasums = await getNodeShasums('8.9.4');
- expect(shasums).to.have.property('node-v8.9.4.tar.gz');
- });
-});
+/** @public types */
+export {
+ Vis,
+ visFactory,
+ DefaultEditorSize,
+ VisParams,
+ VisProvider,
+ VisState,
+ // VisualizationController,
+ // VisType,
+ VisTypeAlias,
+ VisTypesRegistry,
+ Status,
+} from './types';
+
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new VisualizationsPublicPlugin(initializerContext);
+}
+
+export { VisualizationsPublicPlugin as Plugin };
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts
new file mode 100644
index 0000000000000..04a49294bd0c6
--- /dev/null
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy.ts
@@ -0,0 +1,50 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/* eslint-disable @kbn/eslint/no-restricted-paths */
+import { npSetup, npStart } from 'ui/new_platform';
+// @ts-ignore
+import { VisFiltersProvider, createFilter } from 'ui/vis/vis_filters';
+// @ts-ignore
+import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
+// @ts-ignore
+import { VisProvider as Vis } from 'ui/vis/index.js';
+// @ts-ignore
+import { VisFactoryProvider } from 'ui/vis/vis_factory';
+import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
+/* eslint-enable @kbn/eslint/no-restricted-paths */
+
+import { visTypeAliasRegistry } from './types/vis_type_alias_registry';
+
+import { plugin } from '.';
+
+const pluginInstance = plugin({} as any);
+
+export const setup = pluginInstance.setup(npSetup.core, {
+ __LEGACY: {
+ VisFiltersProvider,
+ createFilter,
+
+ Vis,
+ VisFactoryProvider,
+ VisTypesRegistryProvider,
+ defaultFeedbackMessage,
+ visTypeAliasRegistry,
+ },
+});
+export const start = pluginInstance.start(npStart.core);
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
new file mode 100644
index 0000000000000..df5e4d25dedcc
--- /dev/null
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
@@ -0,0 +1,92 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* eslint-disable @kbn/eslint/no-restricted-paths */
+jest.mock('ui/vis/vis_filters');
+jest.mock('ui/vis/default_feedback_message');
+jest.mock('ui/vis/index.js');
+jest.mock('ui/vis/vis_factory');
+jest.mock('ui/registry/vis_types');
+// @ts-ignore
+import { VisFiltersProvider, createFilter } from 'ui/vis/vis_filters';
+// @ts-ignore
+import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
+// @ts-ignore
+import { VisProvider as Vis } from 'ui/vis/index.js';
+// @ts-ignore
+import { VisFactoryProvider } from 'ui/vis/vis_factory';
+import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
+/* eslint-enable @kbn/eslint/no-restricted-paths */
+jest.mock('./types/vis_type_alias_registry');
+import { visTypeAliasRegistry } from './types/vis_type_alias_registry';
+
+import { Plugin } from '.';
+import { coreMock } from '../../../../../../core/public/mocks';
+
+export type Setup = jest.Mocked>;
+export type Start = jest.Mocked>;
+
+const createSetupContract = (): Setup => ({
+ filters: {
+ VisFiltersProvider: jest.fn(),
+ createFilter: jest.fn(),
+ },
+ types: {
+ Vis,
+ VisFactoryProvider: jest.fn(),
+ registerVisualization: jest.fn(),
+ defaultFeedbackMessage,
+ visTypeAliasRegistry: {
+ add: jest.fn(),
+ get: jest.fn(),
+ },
+ },
+});
+
+const createStartContract = (): Start => {};
+
+const createInstance = () => {
+ const plugin = new Plugin({} as any);
+
+ const setup = plugin.setup(coreMock.createSetup(), {
+ __LEGACY: {
+ VisFiltersProvider,
+ createFilter,
+
+ Vis,
+ VisFactoryProvider,
+ VisTypesRegistryProvider,
+ defaultFeedbackMessage,
+ visTypeAliasRegistry,
+ },
+ });
+ const doStart = () => plugin.start(coreMock.createStart());
+
+ return {
+ plugin,
+ setup,
+ doStart,
+ };
+};
+
+export const visualizationsPluginMock = {
+ createSetupContract,
+ createStartContract,
+ createInstance,
+};
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
new file mode 100644
index 0000000000000..abf5974b77532
--- /dev/null
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
@@ -0,0 +1,88 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public';
+
+import { FiltersService, FiltersSetup } from './filters';
+import { TypesService, TypesSetup } from './types';
+import { VisTypeAliasRegistry } from './types/vis_type_alias_registry';
+
+interface SetupDependencies {
+ __LEGACY: {
+ VisFiltersProvider: any;
+ createFilter: any;
+
+ Vis: any;
+ VisFactoryProvider: any;
+ VisTypesRegistryProvider: any;
+ defaultFeedbackMessage: any;
+ visTypeAliasRegistry: VisTypeAliasRegistry;
+ };
+}
+
+export interface Setup {
+ filters: FiltersSetup;
+ types: TypesSetup;
+}
+
+export type Start = void;
+
+export class VisualizationsPublicPlugin implements Plugin {
+ private readonly filters: FiltersService;
+ private readonly types: TypesService;
+
+ constructor(initializerContext: PluginInitializerContext) {
+ this.filters = new FiltersService();
+ this.types = new TypesService();
+ }
+
+ public setup(core: CoreSetup, { __LEGACY }: SetupDependencies) {
+ const {
+ VisFiltersProvider,
+ createFilter,
+ Vis,
+ VisFactoryProvider,
+ VisTypesRegistryProvider,
+ defaultFeedbackMessage,
+ visTypeAliasRegistry,
+ } = __LEGACY;
+
+ return {
+ filters: this.filters.setup({
+ VisFiltersProvider,
+ createFilter,
+ }),
+ types: this.types.setup({
+ Vis,
+ VisFactoryProvider,
+ VisTypesRegistryProvider,
+ defaultFeedbackMessage,
+ visTypeAliasRegistry,
+ }),
+ };
+ }
+
+ public start(core: CoreStart) {
+ // Do nothing yet...
+ }
+
+ public stop() {
+ this.filters.stop();
+ this.types.stop();
+ }
+}
diff --git a/src/legacy/core_plugins/visualizations/public/types/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/types/index.ts
similarity index 100%
rename from src/legacy/core_plugins/visualizations/public/types/index.ts
rename to src/legacy/core_plugins/visualizations/public/np_ready/public/types/index.ts
diff --git a/src/legacy/core_plugins/visualizations/public/types/types_service.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/types/types_service.ts
similarity index 72%
rename from src/legacy/core_plugins/visualizations/public/types/types_service.ts
rename to src/legacy/core_plugins/visualizations/public/np_ready/public/types/types_service.ts
index cb5328812b406..28574917ba1bb 100644
--- a/src/legacy/core_plugins/visualizations/public/types/types_service.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/types/types_service.ts
@@ -17,18 +17,23 @@
* under the License.
*/
+/* eslint-disable @kbn/eslint/no-restricted-paths */
// @ts-ignore
-import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message';
-// @ts-ignore
-import { VisProvider as Vis } from 'ui/vis/index.js';
-// @ts-ignore
-import { VisFactoryProvider, visFactory } from 'ui/vis/vis_factory';
+import { visFactory } from 'ui/vis/vis_factory';
// @ts-ignore
import { DefaultEditorSize } from 'ui/vis/editor_size';
-import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
import * as types from 'ui/vis/vis';
+/* eslint-enable @kbn/eslint/no-restricted-paths */
-import { visTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry';
+import { VisTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry';
+
+interface SetupDependencies {
+ Vis: any;
+ VisFactoryProvider: any;
+ VisTypesRegistryProvider: any;
+ defaultFeedbackMessage: any;
+ visTypeAliasRegistry: VisTypeAliasRegistry;
+}
/**
* Vis Types Service
@@ -36,11 +41,19 @@ import { visTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry';
* @internal
*/
export class TypesService {
- public setup() {
+ public setup({
+ Vis,
+ VisFactoryProvider,
+ VisTypesRegistryProvider,
+ defaultFeedbackMessage,
+ visTypeAliasRegistry,
+ }: SetupDependencies) {
return {
Vis,
VisFactoryProvider,
- VisTypesRegistryProvider,
+ registerVisualization: (registerFn: () => any) => {
+ VisTypesRegistryProvider.register(registerFn);
+ },
defaultFeedbackMessage, // make default in base vis type, or move?
visTypeAliasRegistry,
};
diff --git a/src/legacy/core_plugins/visualizations/public/types/vis_type_alias_registry.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts
similarity index 90%
rename from src/legacy/core_plugins/visualizations/public/types/vis_type_alias_registry.ts
rename to src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts
index 91040cf966567..eb84f93a0d3ba 100644
--- a/src/legacy/core_plugins/visualizations/public/types/vis_type_alias_registry.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/types/vis_type_alias_registry.ts
@@ -52,7 +52,12 @@ export interface VisTypeAlias {
const registry: VisTypeAlias[] = [];
-export const visTypeAliasRegistry = {
+export interface VisTypeAliasRegistry {
+ get: () => VisTypeAlias[];
+ add: (newVisTypeAlias: VisTypeAlias) => void;
+}
+
+export const visTypeAliasRegistry: VisTypeAliasRegistry = {
get: () => [...registry],
add: (newVisTypeAlias: VisTypeAlias) => {
if (registry.find(visTypeAlias => visTypeAlias.name === newVisTypeAlias.name)) {
diff --git a/src/legacy/ui/field_formats/__tests__/field_format.js b/src/legacy/ui/field_formats/__tests__/field_format.js
deleted file mode 100644
index bf8ff9f33b18c..0000000000000
--- a/src/legacy/ui/field_formats/__tests__/field_format.js
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import expect from '@kbn/expect';
-import { asPrettyString } from '../../../core_plugins/kibana/common/utils/as_pretty_string';
-import { FieldFormat } from '../field_format';
-
-describe('FieldFormat class', function () {
-
- let TestFormat;
-
- beforeEach(function () {
- TestFormat = class _TestFormat extends FieldFormat {
- static id = 'test-format';
- static title = 'Test Format';
- _convert(val) {
- return asPrettyString(val);
- }
- };
- });
-
- describe('params', function () {
- it('accepts its params via the constructor', function () {
- const f = new TestFormat({ foo: 'bar' });
- expect(f.param('foo')).to.be('bar');
- });
-
- it('allows reading a clone of the params', function () {
- const params = { foo: 'bar' };
- const f = new TestFormat(params);
- const output = f.params();
- expect(output).to.eql(params);
- expect(output).to.not.be(params);
- });
- });
-
- describe('type', function () {
- it('links the constructor class to instances as the `type`', function () {
- const f = new TestFormat();
- expect(f.type).to.be(TestFormat);
- });
- });
-
- describe('toJSON', function () {
- it('serializes to a version a basic id and param pair', function () {
- const f = new TestFormat({ foo: 'bar' });
- const ser = JSON.parse(JSON.stringify(f));
- expect(ser).to.eql({ id: 'test-format', params: { foo: 'bar' } });
- });
-
- it('removes param values that match the defaults', function () {
- TestFormat.prototype.getParamDefaults = function () {
- return { foo: 'bar' };
- };
-
- const f = new TestFormat({ foo: 'bar', baz: 'bar' });
- const ser = JSON.parse(JSON.stringify(f));
- expect(ser.params).to.eql({ baz: 'bar' });
- });
-
- it('removes the params entirely if they are empty', function () {
- const f = new TestFormat();
- const ser = JSON.parse(JSON.stringify(f));
- expect(ser).to.not.have.property('params');
- });
- });
-
- describe('converters', function () {
- describe('#getConverterFor', function () {
- it('returns a converter for a specific content type', function () {
- const f = new TestFormat();
- expect(f.getConverterFor('html')()).to.be.a('string');
- expect(f.getConverterFor('text')()).to.be.a('string');
- });
- });
-
- describe('#_convert, the instance method or methods used to format values', function () {
- it('can be a function, which gets converted to a text and html converter', function () {
- TestFormat.prototype._convert = function () {
- return 'formatted';
- };
-
- const f = new TestFormat();
- const text = f.getConverterFor('text');
- const html = f.getConverterFor('html');
- expect(text).to.not.be(html);
- expect(text('formatted')).to.be('formatted');
- expect(html('formatted')).to.be('formatted');
- });
-
- it('can be an object, with separate text and html converter', function () {
- TestFormat.prototype._convert = {
- text: _.constant('formatted text'),
- html: _.constant('formatted html'),
- };
-
- const f = new TestFormat();
- const text = f.getConverterFor('text');
- const html = f.getConverterFor('html');
- expect(text).to.not.be(html);
- expect(text('formatted text')).to.be('formatted text');
- expect(html('formatted html')).to.be('formatted html');
- });
-
- it('does not escape the output of the text converter', function () {
- TestFormat.prototype._convert = _.constant('');
- const f = new TestFormat();
- expect(f.convert('', 'text')).to.contain('<');
- });
-
- it('does escape the output of the text converter if used in an html context', function () {
- TestFormat.prototype._convert = _.constant('');
- const f = new TestFormat();
- expect(_.trimRight(_.trimLeft(f.convert('', 'html'), ''), ''))
- .to.not.contain('<');
- });
-
- it('does not escape the output of an html specific converter', function () {
- TestFormat.prototype._convert = {
- text: _.constant(''),
- html: _.constant(''),
- };
-
- const f = new TestFormat();
- expect(f.convert('', 'text')).to.be('');
- expect(f.convert('', 'html')).to.be('');
- });
- });
-
- describe('#convert', function () {
- it('formats a value, defaulting to text content type', function () {
- TestFormat.prototype._convert = {
- text: _.constant('text'),
- html: _.constant('html'),
- };
-
- const f = new TestFormat();
- expect(f.convert('val')).to.be('text');
- });
-
- it('formats a value as html, when specified via second param', function () {
- TestFormat.prototype._convert = {
- text: _.constant('text'),
- html: _.constant('html'),
- };
-
- const f = new TestFormat();
- expect(f.convert('val', 'html')).to.be('html');
- });
-
- it('formats a value as " - " when no value is specified', function () {
- const f = new TestFormat();
- expect(f.convert()).to.be(' - ');
- });
-
- it('formats a list of values as text', function () {
- const f = new TestFormat();
- expect(f.convert(['one', 'two', 'three'])).to.be('["one","two","three"]');
- });
- });
-
- });
-});
diff --git a/src/legacy/ui/field_formats/__tests__/field_formats_mixin.js b/src/legacy/ui/field_formats/__tests__/field_formats_mixin.js
deleted file mode 100644
index 8f513fe28c98b..0000000000000
--- a/src/legacy/ui/field_formats/__tests__/field_formats_mixin.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import expect from '@kbn/expect';
-import sinon from 'sinon';
-
-import { FieldFormat } from '../field_format';
-import * as FieldFormatsServiceNS from '../field_formats_service';
-import { fieldFormatsMixin } from '../field_formats_mixin';
-
-describe('server.registerFieldFormat(createFormat)', () => {
- const sandbox = sinon.createSandbox();
-
- let registerFieldFormat;
- let fieldFormatServiceFactory;
- const serverMock = { decorate() {} };
- beforeEach(async () => {
- sandbox.stub(serverMock);
- await fieldFormatsMixin({}, serverMock);
- [[,, fieldFormatServiceFactory], [,, registerFieldFormat]] = serverMock.decorate.args;
- });
-
- afterEach(() => sandbox.restore());
-
- it('throws if createFormat is not a function', () => {
- expect(() => registerFieldFormat()).to.throwError(error => {
- expect(error.message).to.match(/createFormat is not a function/i);
- });
- });
-
- it('calls the createFormat() function with the FieldFormat class', () => {
- const createFormat = sinon.stub();
- registerFieldFormat(createFormat);
- sinon.assert.calledOnce(createFormat);
- sinon.assert.calledWithExactly(createFormat, sinon.match.same(FieldFormat));
- });
-
- it('passes the returned class to the FieldFormatsService', async () => {
- const { FieldFormatsService: ActualFFS } = FieldFormatsServiceNS;
- sandbox.stub(FieldFormatsServiceNS, 'FieldFormatsService').callsFake((...args) => {
- return new ActualFFS(...args);
- });
-
- const { FieldFormatsService } = FieldFormatsServiceNS;
- class FooFormat {
- static id = 'foo'
- }
- registerFieldFormat(() => FooFormat);
-
- const fieldFormats = await fieldFormatServiceFactory({
- getAll: () => ({}),
- getDefaults: () => ({})
- });
-
- sinon.assert.calledOnce(FieldFormatsService);
- sinon.assert.calledWithExactly(
- FieldFormatsService,
- // array of fieldFormat classes
- [sinon.match.same(FooFormat)],
- // getConfig() function
- sinon.match.func
- );
-
- const format = fieldFormats.getInstance({ id: 'foo' });
- expect(format).to.be.a(FooFormat);
- });
-});
diff --git a/src/legacy/ui/field_formats/__tests__/field_formats_service.js b/src/legacy/ui/field_formats/__tests__/field_formats_service.js
deleted file mode 100644
index 9ae80462c2a07..0000000000000
--- a/src/legacy/ui/field_formats/__tests__/field_formats_service.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import expect from '@kbn/expect';
-import { FieldFormat } from '../field_format';
-import { FieldFormatsService } from '../field_formats_service';
-import { createNumberFormat } from '../../../core_plugins/kibana/common/field_formats/types/number';
-
-describe('FieldFormatsService', function () {
-
- const config = {};
- config['format:defaultTypeMap'] = {
- 'number': { 'id': 'number', 'params': {} },
- '_default_': { 'id': 'string', 'params': {} }
- };
- config['format:number:defaultPattern'] = '0,0.[000]';
- const getConfig = (key) => config[key];
- const fieldFormatClasses = [createNumberFormat(FieldFormat)];
-
- let fieldFormats;
- beforeEach(function () {
- fieldFormats = new FieldFormatsService(fieldFormatClasses, getConfig);
- });
-
- it('FieldFormats are accessible via getType method', function () {
- const Type = fieldFormats.getType('number');
- expect(Type.id).to.be('number');
- });
-
- it('getDefaultInstance returns default FieldFormat instance for fieldType', function () {
- const instance = fieldFormats.getDefaultInstance('number', getConfig);
- expect(instance.type.id).to.be('number');
- expect(instance.convert('0.33333')).to.be('0.333');
- });
-
-});
diff --git a/src/legacy/ui/field_formats/content_types.js b/src/legacy/ui/field_formats/content_types.js
deleted file mode 100644
index 39a5cac7b9027..0000000000000
--- a/src/legacy/ui/field_formats/content_types.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import { asPrettyString } from '../../core_plugins/kibana/common/utils/as_pretty_string';
-import { getHighlightHtml } from '../../core_plugins/kibana/common/highlight/highlight_html';
-
-const types = {
- html: function (format, convert) {
- function recurse(value, field, hit, meta) {
- if (value == null) {
- return asPrettyString(value);
- }
-
- if (!value || typeof value.map !== 'function') {
- return convert.call(format, value, field, hit, meta);
- }
-
- const subVals = value.map(v => {
- return recurse(v, field, hit, meta);
- });
- const useMultiLine = subVals.some(sub => {
- return sub.indexOf('\n') > -1;
- });
-
- return subVals.join(',' + (useMultiLine ? '\n' : ' '));
- }
-
- return function (...args) {
- return `${recurse(...args)}`;
- };
- },
-
- text: function (format, convert) {
- return function recurse(value) {
- if (!value || typeof value.map !== 'function') {
- return convert.call(format, value);
- }
-
- // format a list of values. In text contexts we just use JSON encoding
- return JSON.stringify(value.map(recurse));
- };
- }
-};
-
-function fallbackText(value) {
- return asPrettyString(value);
-}
-
-function fallbackHtml(value, field, hit) {
- const formatted = _.escape(this.convert(value, 'text'));
-
- if (!hit || !hit.highlight || !hit.highlight[field.name]) {
- return formatted;
- } else {
- return getHighlightHtml(formatted, hit.highlight[field.name]);
- }
-}
-
-export function contentTypesSetup(format) {
- const src = format._convert || {};
- const converters = format._convert = {};
-
- converters.text = types.text(format, src.text || fallbackText);
- converters.html = types.html(format, src.html || fallbackHtml);
-
- return format._convert;
-}
diff --git a/src/legacy/ui/field_formats/content_types/html_content_type.ts b/src/legacy/ui/field_formats/content_types/html_content_type.ts
new file mode 100644
index 0000000000000..21cdb21d16895
--- /dev/null
+++ b/src/legacy/ui/field_formats/content_types/html_content_type.ts
@@ -0,0 +1,76 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { escape, isFunction } from 'lodash';
+import { FieldFormatConvert, IFieldFormat, HtmlConventTypeConvert } from '../types';
+
+// @ts-ignore
+import { asPrettyString } from '../../../core_plugins/kibana/common/utils/as_pretty_string';
+// @ts-ignore
+import { getHighlightHtml } from '../../../core_plugins/kibana/common/highlight/highlight_html';
+
+const CONTEXT_TYPE = 'html';
+
+const getConvertFn = (
+ format: IFieldFormat,
+ fieldFormatConvert: FieldFormatConvert
+): HtmlConventTypeConvert => {
+ const fallbackHtml: HtmlConventTypeConvert = (value, field, hit) => {
+ const formatted = escape(format.convert(value, 'text'));
+
+ return !field || !hit || !hit.highlight || !hit.highlight[field.name]
+ ? formatted
+ : getHighlightHtml(formatted, hit.highlight[field.name]);
+ };
+
+ return (fieldFormatConvert[CONTEXT_TYPE] || fallbackHtml) as HtmlConventTypeConvert;
+};
+
+export const setup = (
+ format: IFieldFormat,
+ fieldFormatConvert: FieldFormatConvert
+): FieldFormatConvert => {
+ const convert = getConvertFn(format, fieldFormatConvert);
+
+ const recurse: HtmlConventTypeConvert = (value, field, hit, meta) => {
+ if (value == null) {
+ return asPrettyString(value);
+ }
+
+ if (!value || !isFunction(value.map)) {
+ return convert.call(format, value, field, hit, meta);
+ }
+
+ const subValues = value.map((v: any) => {
+ return recurse(v, field, hit, meta);
+ });
+ const useMultiLine = subValues.some((sub: any) => {
+ return sub.indexOf('\n') > -1;
+ });
+
+ return subValues.join(',' + (useMultiLine ? '\n' : ' '));
+ };
+
+ const wrap: HtmlConventTypeConvert = (value, field, hit, meta) => {
+ return `${recurse(value, field, hit, meta)}`;
+ };
+
+ return {
+ [CONTEXT_TYPE]: wrap,
+ };
+};
diff --git a/src/legacy/ui/field_formats/content_types/index.ts b/src/legacy/ui/field_formats/content_types/index.ts
new file mode 100644
index 0000000000000..b5d98a7bc8393
--- /dev/null
+++ b/src/legacy/ui/field_formats/content_types/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export { setup as textContentTypeSetup } from './text_content_type';
+export { setup as htmlContentTypeSetup } from './html_content_type';
diff --git a/src/legacy/ui/field_formats/content_types/text_content_type.ts b/src/legacy/ui/field_formats/content_types/text_content_type.ts
new file mode 100644
index 0000000000000..0a6983f8f1a87
--- /dev/null
+++ b/src/legacy/ui/field_formats/content_types/text_content_type.ts
@@ -0,0 +1,47 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { isFunction } from 'lodash';
+import { IFieldFormat, FieldFormatConvert, TextContextTypeConvert } from '../types';
+
+// @ts-ignore
+import { asPrettyString } from '../../../core_plugins/kibana/common/utils/as_pretty_string';
+
+const CONTEXT_TYPE = 'text';
+
+const getConvertFn = (fieldFormatConvert: FieldFormatConvert): TextContextTypeConvert =>
+ (fieldFormatConvert[CONTEXT_TYPE] || asPrettyString) as TextContextTypeConvert;
+
+export const setup = (
+ format: IFieldFormat,
+ fieldFormatConvert: FieldFormatConvert
+): FieldFormatConvert => {
+ const convert = getConvertFn(fieldFormatConvert);
+
+ const recurse: TextContextTypeConvert = value => {
+ if (!value || !isFunction(value.map)) {
+ return convert.call(format, value);
+ }
+
+ // format a list of values. In text contexts we just use JSON encoding
+ return JSON.stringify(value.map(recurse));
+ };
+
+ return { [CONTEXT_TYPE]: recurse };
+};
diff --git a/src/legacy/ui/field_formats/converters/custom.ts b/src/legacy/ui/field_formats/converters/custom.ts
new file mode 100644
index 0000000000000..bc9b421127228
--- /dev/null
+++ b/src/legacy/ui/field_formats/converters/custom.ts
@@ -0,0 +1,32 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { FieldFormat } from '../field_format';
+import { FieldFormatConvert } from '../types';
+
+const ID = 'custom';
+
+export const createCustomFieldFormat = (convert: FieldFormatConvert) =>
+ class CustomFieldFormat extends FieldFormat {
+ static id = ID;
+
+ public get _convert() {
+ return convert;
+ }
+ };
diff --git a/src/legacy/ui/field_formats/field_format.js b/src/legacy/ui/field_formats/field_format.js
deleted file mode 100644
index 21e4946a86b48..0000000000000
--- a/src/legacy/ui/field_formats/field_format.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import { contentTypesSetup } from './content_types';
-
-export function FieldFormat(params) {
- // give the constructor a more appropriate name
- this.type = this.constructor;
-
- // keep the params and defaults separate
- this._params = params || {};
-
- // one content type, so assume text
- if (_.isFunction(this._convert)) {
- this._convert = { text: this._convert };
- }
-
- contentTypesSetup(this);
-}
-
-FieldFormat.from = function (converter) {
- class FieldFormatFromConverter extends FieldFormat {}
- FieldFormatFromConverter.prototype._convert = converter;
- return FieldFormatFromConverter;
-};
-
-/**
- * Convert a raw value to a formated string
- * @param {any} value
- * @param {string} [contentType=text] - optional content type, the only two contentTypes
- * currently supported are "html" and "text", which helps
- * formatters adjust to different contexts
- * @return {string} - the formatted string, which is assumed to be html, safe for
- * injecting into the DOM or a DOM attribute
- */
-FieldFormat.prototype.convert = function (value, contentType) {
- return this.getConverterFor(contentType)(value);
-};
-
-/**
- * Get a convert function that is bound to a specific contentType
- * @param {string} [contentType=text]
- * @return {function} - a bound converter function
- */
-FieldFormat.prototype.getConverterFor = function (contentType) {
- return this._convert[contentType || 'text'];
-};
-
-/**
- * Get parameter defaults
- * @return {object} - parameter defaults
- */
-FieldFormat.prototype.getParamDefaults = function () {
- return {};
-};
-
-/**
- * Get the value of a param. This value may be a default value.
- *
- * @param {string} name - the param name to fetch
- * @return {any}
- */
-FieldFormat.prototype.param = function (name) {
- const val = this._params[name];
- if (val || val === false || val === 0) {
- // truthy, false, or 0 are fine
- // '', NaN, null, undefined, etc are not
- return val;
- }
-
- return this.getParamDefaults()[name];
-};
-
-/**
- * Get all of the params in a single object
- * @return {object}
- */
-FieldFormat.prototype.params = function () {
- return _.cloneDeep(_.defaults({}, this._params, this.getParamDefaults()));
-};
-
-/**
- * serialize this format to a simple POJO, with only the params
- * that are not default
- *
- * @return {object}
- */
-FieldFormat.prototype.toJSON = function () {
- const type = this.type;
- const defaults = this.getParamDefaults();
-
- let params = _.transform(this._params, function (uniqParams, val, param) {
- if (val !== defaults[param]) {
- uniqParams[param] = val;
- }
- }, {});
-
- if (!_.size(params)) {
- params = undefined;
- }
-
- return {
- id: type.id,
- params: params
- };
-};
diff --git a/src/legacy/ui/field_formats/field_format.test.ts b/src/legacy/ui/field_formats/field_format.test.ts
new file mode 100644
index 0000000000000..05912d5b5f4a8
--- /dev/null
+++ b/src/legacy/ui/field_formats/field_format.test.ts
@@ -0,0 +1,187 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { constant, trimRight, trimLeft, get } from 'lodash';
+import { FieldFormat } from './field_format';
+import { FieldFormatConvert } from './types';
+
+// @ts-ignore
+import { asPrettyString } from '../../core_plugins/kibana/common/utils/as_pretty_string';
+
+const getTestFormat = (
+ _convert: FieldFormatConvert = {
+ text: (val: string) => asPrettyString(val),
+ },
+ _params?: any
+) =>
+ new (class TestFormat extends FieldFormat {
+ static id = 'test-format';
+ static title = 'Test Format';
+
+ public get _convert() {
+ return _convert;
+ }
+ })(_params);
+
+describe('FieldFormat class', () => {
+ describe('params', () => {
+ test('accepts its params via the constructor', () => {
+ const f = getTestFormat(undefined, { foo: 'bar' });
+ const fooParam = f.param('foo');
+
+ expect(fooParam).toBe('bar');
+ });
+
+ test('allows reading a clone of the params', () => {
+ const params = { foo: 'bar' };
+ const f = getTestFormat(undefined, params);
+ const output = f.params();
+
+ expect(output).toEqual(params);
+ expect(output).not.toBe(params);
+ });
+ });
+
+ describe('type', () => {
+ test('links the constructor class to instances as the `type`', () => {
+ const f = getTestFormat();
+
+ expect(get(f.type, 'id')).toBe('test-format');
+ expect(get(f.type, 'title')).toBe('Test Format');
+ });
+ });
+
+ describe('toJSON', () => {
+ it('serializes to a version a basic id and param pair', () => {
+ const f = getTestFormat(undefined, { foo: 'bar' });
+ const ser = JSON.parse(JSON.stringify(f));
+
+ expect(ser).toEqual({ id: 'test-format', params: { foo: 'bar' } });
+ });
+
+ it('removes the params entirely if they are empty', () => {
+ const f = getTestFormat();
+ const ser = JSON.parse(JSON.stringify(f));
+
+ expect(ser).not.toHaveProperty('params');
+ });
+ });
+
+ describe('converters', () => {
+ describe('#getConverterFor', () => {
+ it('returns a converter for a specific content type', () => {
+ const f = getTestFormat();
+ const htmlConverter = f.getConverterFor('html');
+ const textConverter = f.getConverterFor('text');
+
+ expect(htmlConverter && typeof htmlConverter('')).toBe('string');
+ expect(textConverter && typeof textConverter('')).toBe('string');
+ });
+ });
+
+ describe('#_convert, the instance method or methods used to format values', () => {
+ it('can be a function, which gets converted to a text and html converter', () => {
+ const f = getTestFormat({
+ text: () => 'formatted',
+ });
+ const text = f.getConverterFor('text');
+ const html = f.getConverterFor('html');
+
+ expect(text).not.toBe(html);
+ expect(text && text('formatted')).toBe('formatted');
+ expect(html && html('formatted')).toBe('formatted');
+ });
+
+ it('can be an object, with separate text and html converter', () => {
+ const f = getTestFormat({
+ text: constant('formatted text'),
+ html: constant('formatted html'),
+ });
+ const text = f.getConverterFor('text');
+ const html = f.getConverterFor('html');
+
+ expect(text).not.toBe(html);
+ expect(text && text('formatted text')).toBe('formatted text');
+ expect(html && html('formatted html')).toBe('formatted html');
+ });
+
+ it('does not escape the output of the text converter', () => {
+ const f = getTestFormat({
+ text: constant(''),
+ });
+
+ expect(f.convert('', 'text')).toContain('<');
+ });
+
+ it('does escape the output of the text converter if used in an html context', () => {
+ const f = getTestFormat({
+ text: constant(''),
+ });
+
+ const expected = trimRight(
+ trimLeft(f.convert('', 'html'), ''),
+ ''
+ );
+
+ expect(expected).not.toContain('<');
+ });
+
+ it('does not escape the output of an html specific converter', function() {
+ const f = getTestFormat({
+ text: constant(''),
+ html: constant(''),
+ });
+ expect(f.convert('', 'text')).toBe('');
+ expect(f.convert('', 'html')).toBe('');
+ });
+ });
+
+ describe('#convert', () => {
+ it('formats a value, defaulting to text content type', () => {
+ const f = getTestFormat({
+ text: constant('text'),
+ html: constant('html'),
+ });
+
+ expect(f.convert('val')).toBe('text');
+ });
+
+ it('formats a value as html, when specified via second param', () => {
+ const f = getTestFormat({
+ text: constant('text'),
+ html: constant('html'),
+ });
+
+ expect(f.convert('val', 'html')).toBe('html');
+ });
+
+ it('formats a value as " - " when no value is specified', () => {
+ const f = getTestFormat();
+
+ expect(f.convert(undefined)).toBe(' - ');
+ });
+
+ it('formats a list of values as text', () => {
+ const f = getTestFormat();
+
+ expect(f.convert(['one', 'two', 'three'])).toBe('["one","two","three"]');
+ });
+ });
+ });
+});
diff --git a/src/legacy/ui/field_formats/field_format.ts b/src/legacy/ui/field_formats/field_format.ts
new file mode 100644
index 0000000000000..226631660b8a0
--- /dev/null
+++ b/src/legacy/ui/field_formats/field_format.ts
@@ -0,0 +1,191 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { isFunction, transform, size, cloneDeep, get, defaults } from 'lodash';
+import { createCustomFieldFormat } from './converters/custom';
+import { ContentType, FieldFormatConvert, FieldFormatConvertFunction } from './types';
+
+import { htmlContentTypeSetup, textContentTypeSetup } from './content_types';
+
+const DEFAULT_CONTEXT_TYPE = 'text';
+
+export abstract class FieldFormat {
+ /**
+ * @property {string} - Field Format Id
+ * @static
+ * @public
+ */
+ static id: string;
+ /**
+ * @property {string} - Field Format Title
+ * @static
+ * @public
+ */
+ static title: string;
+
+ /**
+ * @property {string} - Field Format Type
+ * @private
+ */
+ static fieldType: string;
+
+ /**
+ * @property {FieldFormatConvert}
+ * @private
+ */
+ _convert: FieldFormatConvert = FieldFormat.setupContentType(this, get(this, '_convert', {}));
+
+ /**
+ * @property {Function} - ref to child class
+ * @private
+ */
+ type: any = this.constructor;
+
+ constructor(public _params: any = {}) {}
+
+ /**
+ * Convert a raw value to a formatted string
+ * @param {any} value
+ * @param {string} [contentType=text] - optional content type, the only two contentTypes
+ * currently supported are "html" and "text", which helps
+ * formatters adjust to different contexts
+ * @return {string} - the formatted string, which is assumed to be html, safe for
+ * injecting into the DOM or a DOM attribute
+ * @public
+ */
+ convert(value: any, contentType: ContentType = DEFAULT_CONTEXT_TYPE): string {
+ const converter = this.getConverterFor(contentType);
+
+ if (converter) {
+ return converter.call(this, value);
+ }
+
+ return value;
+ }
+
+ /**
+ * Get a convert function that is bound to a specific contentType
+ * @param {string} [contentType=text]
+ * @return {function} - a bound converter function
+ * @public
+ */
+ getConverterFor(
+ contentType: ContentType = DEFAULT_CONTEXT_TYPE
+ ): FieldFormatConvertFunction | null {
+ if (this._convert) {
+ return this._convert[contentType];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get parameter defaults
+ * @return {object} - parameter defaults
+ * @public
+ */
+ getParamDefaults(): Record {
+ return {};
+ }
+
+ /**
+ * Get the value of a param. This value may be a default value.
+ *
+ * @param {string} name - the param name to fetch
+ * @return {any}
+ * @public
+ */
+ param(name: string): any {
+ const val = get(this._params, name);
+
+ if (val || val === false || val === 0) {
+ // truthy, false, or 0 are fine
+ // '', NaN, null, undefined, etc are not
+ return val;
+ }
+
+ return get(this.getParamDefaults(), name);
+ }
+
+ /**
+ * Get all of the params in a single object
+ * @return {object}
+ * @public
+ */
+ params(): Record {
+ return cloneDeep(defaults({}, this._params, this.getParamDefaults()));
+ }
+
+ /**
+ * Serialize this format to a simple POJO, with only the params
+ * that are not default
+ *
+ * @return {object}
+ * @public
+ */
+ toJSON() {
+ const id = get(this.type, 'id');
+ const defaultsParams = this.getParamDefaults() || {};
+
+ const params = transform(
+ this._params,
+ (uniqParams, val, param) => {
+ if (param && val !== get(defaultsParams, param)) {
+ uniqParams[param] = val;
+ }
+ },
+ {}
+ );
+
+ return {
+ id,
+ params: size(params) ? params : undefined,
+ };
+ }
+
+ static from(convertFn: FieldFormatConvertFunction) {
+ return createCustomFieldFormat(FieldFormat.toConvertObject(convertFn));
+ }
+
+ private static setupContentType(
+ fieldFormat: IFieldFormat,
+ convert: FieldFormatConvert | FieldFormatConvertFunction
+ ): FieldFormatConvert {
+ const convertObject = FieldFormat.toConvertObject(convert);
+
+ return {
+ ...textContentTypeSetup(fieldFormat, convertObject),
+ ...htmlContentTypeSetup(fieldFormat, convertObject),
+ };
+ }
+
+ private static toConvertObject(
+ convert: FieldFormatConvert | FieldFormatConvertFunction
+ ): FieldFormatConvert {
+ if (isFunction(convert)) {
+ return {
+ [DEFAULT_CONTEXT_TYPE]: convert,
+ };
+ }
+ return convert;
+ }
+}
+
+export type FieldFormatConvert = { [key: string]: Function } | FieldFormatConvertFunction;
+export type IFieldFormat = PublicMethodsOf;
diff --git a/src/legacy/ui/field_formats/index.js b/src/legacy/ui/field_formats/index.ts
similarity index 92%
rename from src/legacy/ui/field_formats/index.js
rename to src/legacy/ui/field_formats/index.ts
index 1bbad4800a363..cb15ba7a6b0ae 100644
--- a/src/legacy/ui/field_formats/index.js
+++ b/src/legacy/ui/field_formats/index.ts
@@ -17,5 +17,5 @@
* under the License.
*/
-export { fieldFormatsMixin } from './field_formats_mixin';
+export { fieldFormatsMixin } from './mixin/field_formats_mixin';
export { FieldFormat } from './field_format';
diff --git a/src/legacy/ui/field_formats/field_formats_mixin.js b/src/legacy/ui/field_formats/mixin/field_formats_mixin.ts
similarity index 72%
rename from src/legacy/ui/field_formats/field_formats_mixin.js
rename to src/legacy/ui/field_formats/mixin/field_formats_mixin.ts
index 76c72102ebff7..6f20ab8cdf51f 100644
--- a/src/legacy/ui/field_formats/field_formats_mixin.js
+++ b/src/legacy/ui/field_formats/mixin/field_formats_mixin.ts
@@ -17,27 +17,29 @@
* under the License.
*/
-import _ from 'lodash';
+import { has } from 'lodash';
+import { Legacy } from 'kibana';
import { FieldFormatsService } from './field_formats_service';
-import { FieldFormat } from './field_format';
+import { FieldFormat } from '../field_format';
-export function fieldFormatsMixin(kbnServer, server) {
- const fieldFormatClasses = [];
+export function fieldFormatsMixin(kbnServer: any, server: Legacy.Server) {
+ const fieldFormatClasses: FieldFormat[] = [];
// for use outside of the request context, for special cases
- server.decorate('server', 'fieldFormatServiceFactory', async function (uiSettings) {
+ server.decorate('server', 'fieldFormatServiceFactory', async function(uiSettings) {
const uiConfigs = await uiSettings.getAll();
const uiSettingDefaults = await uiSettings.getDefaults();
- Object.keys(uiSettingDefaults).forEach((key) => {
- if (_.has(uiConfigs, key) && uiSettingDefaults[key].type === 'json') {
+ Object.keys(uiSettingDefaults).forEach(key => {
+ if (has(uiConfigs, key) && uiSettingDefaults[key].type === 'json') {
uiConfigs[key] = JSON.parse(uiConfigs[key]);
}
});
- const getConfig = (key) => uiConfigs[key];
+ const getConfig = (key: string) => uiConfigs[key];
+
return new FieldFormatsService(fieldFormatClasses, getConfig);
});
- server.decorate('server', 'registerFieldFormat', (createFormat) => {
+ server.decorate('server', 'registerFieldFormat', createFormat => {
fieldFormatClasses.push(createFormat(FieldFormat));
});
}
diff --git a/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts b/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts
new file mode 100644
index 0000000000000..32f1579510c64
--- /dev/null
+++ b/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts
@@ -0,0 +1,59 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { FieldFormat } from '../field_format';
+import { FieldFormatsService } from './field_formats_service';
+
+// @ts-ignore
+import { createNumberFormat } from '../../../core_plugins/kibana/common/field_formats/types/number';
+
+const getConfig = (key: string) => {
+ switch (key) {
+ case 'format:defaultTypeMap':
+ return {
+ number: { id: 'number', params: {} },
+ _default_: { id: 'string', params: {} },
+ };
+ case 'format:number:defaultPattern':
+ return '0,0.[000]';
+ }
+};
+
+describe('FieldFormatsService', function() {
+ let fieldFormatsService: FieldFormatsService;
+
+ beforeEach(function() {
+ const fieldFormatClasses = [createNumberFormat(FieldFormat)];
+
+ fieldFormatsService = new FieldFormatsService(fieldFormatClasses, getConfig);
+ });
+
+ test('FieldFormats are accessible via getType method', function() {
+ const Type = fieldFormatsService.getType('number');
+
+ expect(Type.id).toBe('number');
+ });
+
+ test('getDefaultInstance returns default FieldFormat instance for fieldType', function() {
+ const instance = fieldFormatsService.getDefaultInstance('number');
+
+ expect(instance.type.id).toBe('number');
+ expect(instance.convert('0.33333')).toBe('0.333');
+ });
+});
diff --git a/src/legacy/ui/field_formats/field_formats_service.js b/src/legacy/ui/field_formats/mixin/field_formats_service.ts
similarity index 66%
rename from src/legacy/ui/field_formats/field_formats_service.js
rename to src/legacy/ui/field_formats/mixin/field_formats_service.ts
index 9584f78046c35..779157f5c418b 100644
--- a/src/legacy/ui/field_formats/field_formats_service.js
+++ b/src/legacy/ui/field_formats/mixin/field_formats_service.ts
@@ -17,11 +17,20 @@
* under the License.
*/
-import _ from 'lodash';
+import { indexBy, Dictionary } from 'lodash';
+import { FieldFormat } from '../field_format';
+
+interface FieldFormatConfig {
+ id: string;
+ params?: Record;
+}
export class FieldFormatsService {
- constructor(fieldFormatClasses, getConfig) {
- this._fieldFormats = _.indexBy(fieldFormatClasses, 'id');
+ getConfig: any;
+ _fieldFormats: Dictionary;
+
+ constructor(fieldFormatClasses: FieldFormat[], getConfig: Function) {
+ this._fieldFormats = indexBy(fieldFormatClasses, 'id');
this.getConfig = getConfig;
}
@@ -30,9 +39,9 @@ export class FieldFormatsService {
* using the format:defaultTypeMap config map
*
* @param {String} fieldType - the field type
- * @return {String}
+ * @return {FieldFormatConfig}
*/
- getDefaultConfig(fieldType) {
+ getDefaultConfig(fieldType: string): FieldFormatConfig {
const defaultMap = this.getConfig('format:defaultTypeMap');
return defaultMap[fieldType] || defaultMap._default_;
}
@@ -43,21 +52,19 @@ export class FieldFormatsService {
* @param {String} fieldType
* @return {FieldFormat}
*/
- getDefaultInstance(fieldType) {
- const conf = this.getDefaultConfig(fieldType);
- const FieldFormat = this._fieldFormats[conf.id];
- return new FieldFormat(conf.params, this.getConfig);
+ getDefaultInstance(fieldType: string): FieldFormat {
+ return this.getInstance(this.getDefaultConfig(fieldType));
}
/**
* Get the fieldFormat instance for a field format configuration.
*
- * @param {Object} conf:id, conf:params
+ * @param {FieldFormatConfig} field format config
* @return {FieldFormat}
*/
- getInstance(conf) {
- const FieldFormat = this._fieldFormats[conf.id];
- return new FieldFormat(conf.params, this.getConfig);
+ getInstance(conf: FieldFormatConfig): FieldFormat {
+ // @ts-ignore
+ return new this._fieldFormats[conf.id](conf.params, this.getConfig);
}
/**
@@ -66,7 +73,7 @@ export class FieldFormatsService {
* @param {String} fieldFormatId - the FieldFormat id
* @return {FieldFormat}
*/
- getType(fieldFormatId) {
+ getType(fieldFormatId: string): any {
return this._fieldFormats[fieldFormatId];
}
}
diff --git a/src/legacy/ui/field_formats/types.ts b/src/legacy/ui/field_formats/types.ts
new file mode 100644
index 0000000000000..f5238422020c2
--- /dev/null
+++ b/src/legacy/ui/field_formats/types.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Field } from '../../core_plugins/data/public/index_patterns';
+
+/** @public **/
+export type ContentType = 'html' | 'text';
+
+/** @public **/
+export { IFieldFormat } from './field_format';
+
+/** @internal **/
+export type HtmlConventTypeConvert = (
+ value: any,
+ field?: Field,
+ hit?: Record,
+ meta?: any
+) => string;
+
+/** @internal **/
+export type TextContextTypeConvert = (value: any) => string;
+
+/** @internal **/
+export type FieldFormatConvertFunction = HtmlConventTypeConvert | TextContextTypeConvert;
+
+/** @internal **/
+export interface FieldFormatConvert {
+ [key: string]: FieldFormatConvertFunction;
+}
diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss
index 1c3a9c006acfd..e7d85b8cc3f8e 100644
--- a/src/legacy/ui/public/_index.scss
+++ b/src/legacy/ui/public/_index.scss
@@ -19,6 +19,7 @@
@import './exit_full_screen/index';
@import './field_editor/index';
@import './notify/index';
+@import './saved_objects/index';
@import './share/index';
@import './style_compile/index';
diff --git a/src/legacy/ui/public/chrome/chrome.js b/src/legacy/ui/public/chrome/chrome.js
index 8f58da9107673..a5a0521013a6e 100644
--- a/src/legacy/ui/public/chrome/chrome.js
+++ b/src/legacy/ui/public/chrome/chrome.js
@@ -95,7 +95,6 @@ const waitForBootstrap = new Promise(resolve => {
document.body.setAttribute('id', `${internals.app.id}-app`);
chrome.setupAngular();
- // targetDomElement.setAttribute('id', 'kibana-body');
targetDomElement.setAttribute('kbn-chrome', 'true');
targetDomElement.setAttribute('ng-class', '{ \'hidden-chrome\': !chrome.getVisible() }');
targetDomElement.className = 'app-wrapper';
diff --git a/src/legacy/ui/public/chrome/directives/kbn_chrome.js b/src/legacy/ui/public/chrome/directives/kbn_chrome.js
index d81a1ceb5f288..755cb8b42d363 100644
--- a/src/legacy/ui/public/chrome/directives/kbn_chrome.js
+++ b/src/legacy/ui/public/chrome/directives/kbn_chrome.js
@@ -77,15 +77,21 @@ export function kbnChromeProvider(chrome, internals) {
// Non-scope based code (e.g., React)
// Banners
- ReactDOM.render(
-
-
- ,
- document.getElementById('globalBannerList')
- );
+ const bannerListContainer = document.getElementById('globalBannerList');
+ // Banners not supported in New Platform yet
+ // https://github.com/elastic/kibana/issues/41986
+ if (bannerListContainer) {
+ ReactDOM.render(
+
+
+ ,
+ bannerListContainer
+ );
+ }
+
return chrome;
}
diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/__snapshots__/string.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/__snapshots__/string.test.js.snap
index 1b48a8e6a7250..5561fbec68b3b 100644
--- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/__snapshots__/string.test.js.snap
+++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/__snapshots__/string.test.js.snap
@@ -58,6 +58,10 @@ exports[`StringFormatEditor should render normally 1`] = `
"input": "SGVsbG8gd29ybGQ=",
"output": "SGVSBG8GD29YBGQ=",
},
+ Object {
+ "input": "%EC%95%88%EB%85%95%20%ED%82%A4%EB%B0%94%EB%82%98",
+ "output": "%EC%95%88%EB%85%95%20%ED%82%A4%EB%B0%94%EB%82%98",
+ },
]
}
/>
diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/string.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/string.js
index 8d3c1d9fca1c8..9aacc7899afee 100644
--- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/string.js
+++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/string/string.js
@@ -44,7 +44,8 @@ export class StringFormatEditor extends DefaultFormatEditor {
'STAY CALM!',
'com.organizations.project.ClassName',
'hostname.net',
- 'SGVsbG8gd29ybGQ='
+ 'SGVsbG8gd29ybGQ=',
+ '%EC%95%88%EB%85%95%20%ED%82%A4%EB%B0%94%EB%82%98'
];
}
diff --git a/src/legacy/ui/public/legacy_compat/angular_config.tsx b/src/legacy/ui/public/legacy_compat/angular_config.tsx
index 1e22003b32833..28d57e9f8e8c9 100644
--- a/src/legacy/ui/public/legacy_compat/angular_config.tsx
+++ b/src/legacy/ui/public/legacy_compat/angular_config.tsx
@@ -33,7 +33,7 @@ import * as Rx from 'rxjs';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { InternalCoreStart } from 'kibana/public';
+import { CoreStart, LegacyCoreStart } from 'kibana/public';
import { fatalError } from 'ui/notify';
import { capabilities } from 'ui/capabilities';
@@ -77,7 +77,7 @@ export const configureAppAngularModule = (angularModule: IModule) => {
.run($setupUrlOverflowHandling(newPlatform));
};
-const getEsUrl = (newPlatform: InternalCoreStart) => {
+const getEsUrl = (newPlatform: CoreStart) => {
const a = document.createElement('a');
a.href = newPlatform.http.basePath.prepend('/elasticsearch');
const protocolPort = /https/.test(a.protocol) ? 443 : 80;
@@ -90,7 +90,7 @@ const getEsUrl = (newPlatform: InternalCoreStart) => {
};
};
-const setupCompileProvider = (newPlatform: InternalCoreStart) => (
+const setupCompileProvider = (newPlatform: LegacyCoreStart) => (
$compileProvider: ICompileProvider
) => {
if (!newPlatform.injectedMetadata.getLegacyMetadata().devMode) {
@@ -98,7 +98,7 @@ const setupCompileProvider = (newPlatform: InternalCoreStart) => (
}
};
-const setupLocationProvider = (newPlatform: InternalCoreStart) => (
+const setupLocationProvider = (newPlatform: CoreStart) => (
$locationProvider: ILocationProvider
) => {
$locationProvider.html5Mode({
@@ -110,7 +110,7 @@ const setupLocationProvider = (newPlatform: InternalCoreStart) => (
$locationProvider.hashPrefix('');
};
-export const $setupXsrfRequestInterceptor = (newPlatform: InternalCoreStart) => {
+export const $setupXsrfRequestInterceptor = (newPlatform: LegacyCoreStart) => {
const version = newPlatform.injectedMetadata.getLegacyMetadata().version;
// Configure jQuery prefilter
@@ -145,7 +145,7 @@ export const $setupXsrfRequestInterceptor = (newPlatform: InternalCoreStart) =>
* @param {HttpService} $http
* @return {undefined}
*/
-const capture$httpLoadingCount = (newPlatform: InternalCoreStart) => (
+const capture$httpLoadingCount = (newPlatform: CoreStart) => (
$rootScope: IRootScopeService,
$http: IHttpService
) => {
@@ -166,7 +166,7 @@ const capture$httpLoadingCount = (newPlatform: InternalCoreStart) => (
* lets us integrate with the angular router so that we can automatically clear
* the breadcrumbs if we switch to a Kibana app that does not use breadcrumbs correctly
*/
-const $setupBreadcrumbsAutoClear = (newPlatform: InternalCoreStart) => (
+const $setupBreadcrumbsAutoClear = (newPlatform: CoreStart) => (
$rootScope: IRootScopeService,
$injector: any
) => {
@@ -213,7 +213,7 @@ const $setupBreadcrumbsAutoClear = (newPlatform: InternalCoreStart) => (
* lets us integrate with the angular router so that we can automatically clear
* the badge if we switch to a Kibana app that does not use the badge correctly
*/
-const $setupBadgeAutoClear = (newPlatform: InternalCoreStart) => (
+const $setupBadgeAutoClear = (newPlatform: CoreStart) => (
$rootScope: IRootScopeService,
$injector: any
) => {
@@ -253,7 +253,7 @@ const $setupBadgeAutoClear = (newPlatform: InternalCoreStart) => (
* the helpExtension if we switch to a Kibana app that does not set its own
* helpExtension
*/
-const $setupHelpExtensionAutoClear = (newPlatform: InternalCoreStart) => (
+const $setupHelpExtensionAutoClear = (newPlatform: CoreStart) => (
$rootScope: IRootScopeService,
$injector: any
) => {
@@ -285,7 +285,7 @@ const $setupHelpExtensionAutoClear = (newPlatform: InternalCoreStart) => (
});
};
-const $setupUrlOverflowHandling = (newPlatform: InternalCoreStart) => (
+const $setupUrlOverflowHandling = (newPlatform: CoreStart) => (
$location: ILocationService,
$rootScope: IRootScopeService,
Private: any,
diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts
index 5e0eb2feeb450..4f55349e3efe2 100644
--- a/src/legacy/ui/public/new_platform/new_platform.ts
+++ b/src/legacy/ui/public/new_platform/new_platform.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { InternalCoreSetup, InternalCoreStart } from '../../../../core/public';
+import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public';
import { Plugin as DataPlugin } from '../../../../plugins/data/public';
import {
Setup as InspectorSetup,
@@ -34,12 +34,12 @@ export interface PluginsStart {
}
export const npSetup = {
- core: (null as unknown) as InternalCoreSetup,
+ core: (null as unknown) as LegacyCoreSetup,
plugins: {} as PluginsSetup,
};
export const npStart = {
- core: (null as unknown) as InternalCoreStart,
+ core: (null as unknown) as LegacyCoreStart,
plugins: {} as PluginsStart,
};
@@ -48,18 +48,18 @@ export const npStart = {
* @internal
*/
export function __reset__() {
- npSetup.core = (null as unknown) as InternalCoreSetup;
+ npSetup.core = (null as unknown) as LegacyCoreSetup;
npSetup.plugins = {} as any;
- npStart.core = (null as unknown) as InternalCoreStart;
+ npStart.core = (null as unknown) as LegacyCoreStart;
npStart.plugins = {} as any;
}
-export function __setup__(coreSetup: InternalCoreSetup, plugins: PluginsSetup) {
+export function __setup__(coreSetup: LegacyCoreSetup, plugins: PluginsSetup) {
npSetup.core = coreSetup;
npSetup.plugins = plugins;
}
-export function __start__(coreStart: InternalCoreStart, plugins: PluginsStart) {
+export function __start__(coreStart: LegacyCoreStart, plugins: PluginsStart) {
npStart.core = coreStart;
npStart.plugins = plugins;
}
diff --git a/src/legacy/ui/public/saved_objects/_index.scss b/src/legacy/ui/public/saved_objects/_index.scss
new file mode 100644
index 0000000000000..50a192b6a7b17
--- /dev/null
+++ b/src/legacy/ui/public/saved_objects/_index.scss
@@ -0,0 +1 @@
+@import '../../../../plugins/kibana_react/public/saved_objects/index';
diff --git a/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.js b/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx
similarity index 52%
rename from src/legacy/ui/public/saved_objects/show_saved_object_save_modal.js
rename to src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx
index bd2fbba3aa145..6aea3c72e0c34 100644
--- a/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.js
+++ b/src/legacy/ui/public/saved_objects/show_saved_object_save_modal.tsx
@@ -21,7 +21,25 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { I18nContext } from 'ui/i18n';
-export function showSaveModal(saveModal) {
+/**
+ * Represents the result of trying to persist the saved object.
+ * Contains `error` prop if something unexpected happened (e.g. network error).
+ * Contains an `id` if persisting was successful. If `id` and
+ * `error` are undefined, persisting was not successful, but the
+ * modal can still recover (e.g. the name of the saved object was already taken).
+ */
+export type SaveResult = { id?: string } | { error: Error };
+
+function isSuccess(result: SaveResult): result is { id?: string } {
+ return 'id' in result;
+}
+
+interface MinimalSaveModalProps {
+ onSave: (...args: any[]) => Promise;
+ onClose: () => void;
+}
+
+export function showSaveModal(saveModal: React.ReactElement) {
const container = document.createElement('div');
const closeModal = () => {
ReactDOM.unmountComponentAtNode(container);
@@ -30,21 +48,19 @@ export function showSaveModal(saveModal) {
const onSave = saveModal.props.onSave;
- const onSaveConfirmed = (...args) => {
- onSave(...args).then(({ id, error }) => {
- if (id || error) {
- closeModal();
- }
- });
+ const onSaveConfirmed: MinimalSaveModalProps['onSave'] = async (...args) => {
+ const response = await onSave(...args);
+ // close modal if we either hit an error or the saved object got an id
+ if (Boolean(isSuccess(response) ? response.id : response.error)) {
+ closeModal();
+ }
+ return response;
};
document.body.appendChild(container);
- const element = React.cloneElement(
- saveModal,
- {
- onSave: onSaveConfirmed,
- onClose: closeModal
- }
- );
+ const element = React.cloneElement(saveModal, {
+ onSave: onSaveConfirmed,
+ onClose: closeModal,
+ });
ReactDOM.render({element}, container);
}
diff --git a/src/legacy/ui/public/timefilter/index.ts b/src/legacy/ui/public/timefilter/index.ts
index 5f5fcf6b19c7f..34f2a367a217c 100644
--- a/src/legacy/ui/public/timefilter/index.ts
+++ b/src/legacy/ui/public/timefilter/index.ts
@@ -17,8 +17,16 @@
* under the License.
*/
-export { TimeRange, RefreshInterval } from '../../../../plugins/data/public';
+import uiRoutes from 'ui/routes';
+import { registerTimefilterWithGlobalState, getTimefilterConfig } from './setup_router';
+import { Timefilter, TimeHistory } from '../../../core_plugins/data/public/timefilter';
-export { timefilter, Timefilter, registerTimefilterWithGlobalState } from './timefilter';
-export { timeHistory, TimeHistory } from './time_history';
-export { getTime } from './get_time';
+const config = getTimefilterConfig();
+
+export { Timefilter, TimeHistory, getTime } from '../../../core_plugins/data/public/timefilter';
+export const timeHistory = new TimeHistory();
+export const timefilter = new Timefilter(config, timeHistory);
+
+uiRoutes.addSetupWork((globalState, $rootScope) => {
+ return registerTimefilterWithGlobalState(timefilter, globalState, $rootScope);
+});
diff --git a/src/legacy/ui/public/timefilter/setup_router.ts b/src/legacy/ui/public/timefilter/setup_router.ts
new file mode 100644
index 0000000000000..cbd03df455da3
--- /dev/null
+++ b/src/legacy/ui/public/timefilter/setup_router.ts
@@ -0,0 +1,89 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import _ from 'lodash';
+import { IScope } from 'angular';
+import moment from 'moment';
+import { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
+import chrome from 'ui/chrome';
+import { RefreshInterval, TimeRange } from 'src/plugins/data/public';
+import { Timefilter } from '../../../core_plugins/data/public/timefilter';
+
+// TODO
+// remove everything underneath once globalState is no longer an angular service
+// and listener can be registered without angular.
+function convertISO8601(stringTime: string): string {
+ const obj = moment(stringTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true);
+ return obj.isValid() ? obj.toString() : stringTime;
+}
+
+export function getTimefilterConfig() {
+ const settings = chrome.getUiSettingsClient();
+ return {
+ timeDefaults: settings.get('timepicker:timeDefaults'),
+ refreshIntervalDefaults: settings.get('timepicker:refreshIntervalDefaults'),
+ };
+}
+
+// Currently some parts of Kibana (index patterns, timefilter) rely on addSetupWork in the uiRouter
+// and require it to be executed to properly function.
+// This function is exposed for applications that do not use uiRoutes like APM
+// Kibana issue https://github.com/elastic/kibana/issues/19110 tracks the removal of this dependency on uiRouter
+export const registerTimefilterWithGlobalState = _.once(
+ (timefilter: Timefilter, globalState: any, $rootScope: IScope) => {
+ // settings have to be re-fetched here, to make sure that settings changed by overrideLocalDefault are taken into account.
+ const config = getTimefilterConfig();
+ timefilter.setTime(_.defaults(globalState.time || {}, config.timeDefaults));
+ timefilter.setRefreshInterval(
+ _.defaults(globalState.refreshInterval || {}, config.refreshIntervalDefaults)
+ );
+
+ globalState.on('fetch_with_changes', () => {
+ // clone and default to {} in one
+ const newTime: TimeRange = _.defaults({}, globalState.time, config.timeDefaults);
+ const newRefreshInterval: RefreshInterval = _.defaults(
+ {},
+ globalState.refreshInterval,
+ config.refreshIntervalDefaults
+ );
+
+ if (newTime) {
+ if (newTime.to) newTime.to = convertISO8601(newTime.to);
+ if (newTime.from) newTime.from = convertISO8601(newTime.from);
+ }
+
+ timefilter.setTime(newTime);
+ timefilter.setRefreshInterval(newRefreshInterval);
+ });
+
+ const updateGlobalStateWithTime = () => {
+ globalState.time = timefilter.getTime();
+ globalState.refreshInterval = timefilter.getRefreshInterval();
+ globalState.save();
+ };
+
+ subscribeWithScope($rootScope, timefilter.getRefreshIntervalUpdate$(), {
+ next: updateGlobalStateWithTime,
+ });
+
+ subscribeWithScope($rootScope, timefilter.getTimeUpdate$(), {
+ next: updateGlobalStateWithTime,
+ });
+ }
+);
diff --git a/src/legacy/ui/public/vis/agg_configs.ts b/src/legacy/ui/public/vis/agg_configs.ts
index e3edbef40c654..e16784bb6e2f2 100644
--- a/src/legacy/ui/public/vis/agg_configs.ts
+++ b/src/legacy/ui/public/vis/agg_configs.ts
@@ -27,8 +27,8 @@
*/
import _ from 'lodash';
+import { TimeRange } from 'src/plugins/data/public';
import { Schemas } from '../visualize/loader/pipeline_helpers/build_pipeline';
-import { TimeRange } from '../timefilter';
import { Schema } from '../vis/editors/default/schemas';
import { AggConfig, AggConfigOptions } from './agg_config';
import { AggGroupNames } from './editors/default/agg_groups';
diff --git a/src/legacy/ui/public/vis/request_handlers/courier.js b/src/legacy/ui/public/vis/request_handlers/courier.js
index cdd5158c5701d..b6f05f6228ea3 100644
--- a/src/legacy/ui/public/vis/request_handlers/courier.js
+++ b/src/legacy/ui/public/vis/request_handlers/courier.js
@@ -24,7 +24,7 @@ import { calculateObjectHash } from '../lib/calculate_object_hash';
import { getRequestInspectorStats, getResponseInspectorStats } from '../../courier/utils/courier_inspector_utils';
import { tabifyAggResponse } from '../../agg_response/tabify/tabify';
import { buildTabularInspectorData } from '../../inspector/build_tabular_inspector_data';
-import { getTime } from '../../timefilter/get_time';
+import { getTime } from '../../timefilter';
const CourierRequestHandlerProvider = function () {
diff --git a/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts b/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts
index 213f1e6252a6d..03751e189210a 100644
--- a/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts
+++ b/src/legacy/ui/public/vis/request_handlers/request_handlers.d.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { TimeRange } from 'ui/timefilter';
+import { TimeRange } from 'src/plugins/data/public';
import { Query } from 'src/legacy/core_plugins/data/public';
import { Filter } from '@kbn/es-query';
import { AggConfigs } from 'ui/vis/agg_configs';
diff --git a/src/legacy/ui/public/visualize/loader/types.ts b/src/legacy/ui/public/visualize/loader/types.ts
index 595a0649ec5ef..bb1113d212261 100644
--- a/src/legacy/ui/public/visualize/loader/types.ts
+++ b/src/legacy/ui/public/visualize/loader/types.ts
@@ -18,7 +18,7 @@
*/
import { Filter } from '@kbn/es-query';
-import { TimeRange } from 'ui/timefilter';
+import { TimeRange } from 'src/plugins/data/public';
import { Query } from 'src/legacy/core_plugins/data/public';
import { SavedObject } from 'ui/saved_objects/saved_object';
diff --git a/src/legacy/ui/ui_bundles/ui_bundles_controller.js b/src/legacy/ui/ui_bundles/ui_bundles_controller.js
index a4521268ea121..5e9ae0abbb183 100644
--- a/src/legacy/ui/ui_bundles/ui_bundles_controller.js
+++ b/src/legacy/ui/ui_bundles/ui_bundles_controller.js
@@ -25,6 +25,7 @@ import { existsSync } from 'fs';
import del from 'del';
import { makeRe } from 'minimatch';
import mkdirp from 'mkdirp';
+import jsonStableStringify from 'json-stable-stringify';
import { IS_KIBANA_DISTRIBUTABLE } from '../../utils';
@@ -48,6 +49,21 @@ function getWebpackAliases(pluginSpecs) {
}, {});
}
+function sortAllArrays(input) {
+ if (Array.isArray(input)) {
+ return input
+ .map(i => sortAllArrays(i))
+ .sort((a, b) => typeof a === 'string' && typeof b === 'string' ? a.localeCompare(b) : 0);
+ }
+
+ if (typeof input === 'object') {
+ return Object.entries(input)
+ .map(([key, value]) => [key, sortAllArrays(value)]);
+ }
+
+ return input;
+}
+
export class UiBundlesController {
constructor(kbnServer) {
const { config, uiApps, uiExports, pluginSpecs } = kbnServer;
@@ -59,9 +75,7 @@ export class UiBundlesController {
sourceMaps: config.get('optimize.sourceMaps'),
kbnVersion: config.get('pkg.version'),
buildNum: config.get('pkg.buildNum'),
- plugins: pluginSpecs
- .map(spec => spec.getId())
- .sort((a, b) => a.localeCompare(b))
+ appExtensions: sortAllArrays(uiExports.appExtensions),
};
this._filter = makeRe(config.get('optimize.bundleFilter') || '*', {
@@ -81,6 +95,13 @@ export class UiBundlesController {
this._postLoaders = [];
this._bundles = [];
+ // create a bundle for core-only with no modules
+ this.add({
+ id: 'core',
+ modules: [],
+ template: appEntryTemplate
+ });
+
// create a bundle for each uiApp
for (const uiApp of uiApps) {
this.add({
@@ -143,7 +164,9 @@ export class UiBundlesController {
}
getContext() {
- return JSON.stringify(this._context, null, ' ');
+ return jsonStableStringify(this._context, {
+ space: ' '
+ });
}
resolvePath(...args) {
diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js
index 47d13184bfd0a..7e6609c4b5487 100644
--- a/src/legacy/ui/ui_render/ui_render_mixin.js
+++ b/src/legacy/ui/ui_render/ui_render_mixin.js
@@ -102,9 +102,7 @@ export function uiRenderMixin(kbnServer, server, config) {
async handler(request, h) {
const { id } = request.params;
const app = server.getUiAppById(id) || server.getHiddenUiAppById(id);
- if (!app) {
- throw Boom.notFound(`Unknown app: ${id}`);
- }
+ const isCore = !app;
const uiSettings = request.getUiSettingsService();
const darkMode = !authEnabled || request.auth.isAuthenticated
@@ -130,7 +128,9 @@ export function uiRenderMixin(kbnServer, server, config) {
),
`${regularBundlePath}/${darkMode ? 'dark' : 'light'}_theme.style.css`,
`${regularBundlePath}/commons.style.css`,
- `${regularBundlePath}/${app.getId()}.style.css`,
+ ...(
+ !isCore ? [`${regularBundlePath}/${app.getId()}.style.css`] : []
+ ),
...kbnServer.uiExports.styleSheetPaths
.filter(path => (
path.theme === '*' || path.theme === (darkMode ? 'dark' : 'light')
@@ -145,7 +145,7 @@ export function uiRenderMixin(kbnServer, server, config) {
const bootstrap = new AppBootstrap({
templateData: {
- appId: app.getId(),
+ appId: isCore ? 'core' : app.getId(),
regularBundlePath,
dllBundlePath,
styleSheetPaths,
@@ -164,12 +164,11 @@ export function uiRenderMixin(kbnServer, server, config) {
});
server.route({
- path: '/app/{id}',
+ path: '/app/{id}/{any*}',
method: 'GET',
async handler(req, h) {
const id = req.params.id;
const app = server.getUiAppById(id);
- if (!app) throw Boom.notFound('Unknown app ' + id);
try {
if (kbnServer.status.isGreen()) {
@@ -183,9 +182,15 @@ export function uiRenderMixin(kbnServer, server, config) {
}
});
- async function getLegacyKibanaPayload({ app, translations, request, includeUserProvidedConfig }) {
+ async function getUiSettings({ request, includeUserProvidedConfig }) {
const uiSettings = request.getUiSettingsService();
+ return props({
+ defaults: uiSettings.getDefaults(),
+ user: includeUserProvidedConfig && uiSettings.getUserProvided()
+ });
+ }
+ async function getLegacyKibanaPayload({ app, translations, request, includeUserProvidedConfig }) {
return {
app,
translations,
@@ -198,16 +203,15 @@ export function uiRenderMixin(kbnServer, server, config) {
basePath: request.getBasePath(),
serverName: config.get('server.name'),
devMode: config.get('env.dev'),
- uiSettings: await props({
- defaults: uiSettings.getDefaults(),
- user: includeUserProvidedConfig && uiSettings.getUserProvided()
- })
+ uiSettings: await getUiSettings({ request, includeUserProvidedConfig }),
};
}
async function renderApp({ app, h, includeUserProvidedConfig = true, injectedVarsOverrides = {} }) {
const request = h.request;
const basePath = request.getBasePath();
+ const uiSettings = await getUiSettings({ request, includeUserProvidedConfig });
+ app = app || { getId: () => 'core' };
const legacyMetadata = await getLegacyKibanaPayload({
app,
@@ -228,13 +232,14 @@ export function uiRenderMixin(kbnServer, server, config) {
bootstrapScriptUrl: `${basePath}/bundles/app/${app.getId()}/bootstrap.js`,
i18n: (id, options) => i18n.translate(id, options),
locale: i18n.getLocale(),
- darkMode: get(legacyMetadata.uiSettings.user, ['theme:darkMode', 'userValue'], false),
+ darkMode: get(uiSettings.user, ['theme:darkMode', 'userValue'], false),
injectedMetadata: {
version: kbnServer.version,
buildNumber: config.get('pkg.buildNum'),
branch: config.get('pkg.branch'),
basePath,
+ legacyMode: app.getId() !== 'core',
i18n: {
translationsUrl: `${basePath}/translations/${i18n.getLocale()}.json`,
},
@@ -245,7 +250,7 @@ export function uiRenderMixin(kbnServer, server, config) {
request,
mergeVariables(
injectedVarsOverrides,
- await server.getInjectedUiAppVars(app.getId()),
+ app ? await server.getInjectedUiAppVars(app.getId()) : {},
defaultInjectedVars,
),
),
diff --git a/src/legacy/ui/ui_render/views/ui_app.pug b/src/legacy/ui/ui_render/views/ui_app.pug
index 5bbcc51e7745c..95b321e09b500 100644
--- a/src/legacy/ui/ui_render/views/ui_app.pug
+++ b/src/legacy/ui/ui_render/views/ui_app.pug
@@ -114,7 +114,7 @@ block content
}
}
- .kibanaWelcomeView(id="kbn_loading_message", style="display: none;")
+ .kibanaWelcomeView(id="kbn_loading_message", style="display: none;", data-test-subj="kbnLoadingMessage")
.kibanaLoaderWrap
.kibanaLoader
.kibanaWelcomeLogoCircle
diff --git a/src/plugins/data/common/expressions/expression_types/kibana_context.ts b/src/plugins/data/common/expressions/expression_types/kibana_context.ts
index 5ee3f7abbbdb0..0a3e8a4db87f6 100644
--- a/src/plugins/data/common/expressions/expression_types/kibana_context.ts
+++ b/src/plugins/data/common/expressions/expression_types/kibana_context.ts
@@ -18,8 +18,8 @@
*/
import { Filter } from '@kbn/es-query';
+import { TimeRange } from 'src/plugins/data/public';
import { Query } from '../../query/types';
-import { TimeRange } from '../../timefilter/types';
const name = 'kibana_context';
diff --git a/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap b/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap
index 47b4a5219068f..5bf5c8be05ed8 100644
--- a/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap
+++ b/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap
@@ -6,7 +6,7 @@ exports[`SavedObjectSaveModal should render matching snapshot 1`] = `
onSubmit={[Function]}
>
-
+ Save
diff --git a/src/plugins/kibana_react/public/saved_objects/_index.scss b/src/plugins/kibana_react/public/saved_objects/_index.scss
new file mode 100644
index 0000000000000..6c773c7f777be
--- /dev/null
+++ b/src/plugins/kibana_react/public/saved_objects/_index.scss
@@ -0,0 +1 @@
+@import './saved_object_save_modal';
diff --git a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.scss b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.scss
new file mode 100644
index 0000000000000..b8758f692b7f7
--- /dev/null
+++ b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.scss
@@ -0,0 +1,3 @@
+.kbnSavedObjectSaveModal {
+ width: $euiSizeXXL * 10;
+}
\ No newline at end of file
diff --git a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx
index 27da5d90646b3..4d4e429417085 100644
--- a/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx
+++ b/src/plugins/kibana_react/public/saved_objects/saved_object_save_modal.tsx
@@ -35,8 +35,9 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
-interface OnSaveProps {
+export interface OnSaveProps {
newTitle: string;
newCopyOnSave: boolean;
isTitleDuplicateConfirmed: boolean;
@@ -72,14 +73,14 @@ export class SavedObjectSaveModal extends React.Component {
};
public render() {
- const { isTitleDuplicateConfirmed, hasTitleDuplicate, title, isLoading } = this.state;
+ const { isTitleDuplicateConfirmed, hasTitleDuplicate, title } = this.state;
return (
@@ -204,6 +190,34 @@ export class SavedObjectSaveModal extends React.Component {
this.saveSavedObject();
};
+ private renderConfirmButton = () => {
+ const { isLoading, title, hasTitleDuplicate } = this.state;
+
+ let confirmLabel: string | React.ReactNode = hasTitleDuplicate
+ ? i18n.translate('kibana-react.savedObjects.saveModal.confirmSaveButtonLabel', {
+ defaultMessage: 'Confirm save',
+ })
+ : i18n.translate('kibana-react.savedObjects.saveModal.saveButtonLabel', {
+ defaultMessage: 'Save',
+ });
+
+ if (this.props.confirmButtonLabel) {
+ confirmLabel = this.props.confirmButtonLabel;
+ }
+
+ return (
+
+ {confirmLabel}
+
+ );
+ };
+
private renderDuplicateTitleCallout = () => {
if (!this.state.hasTitleDuplicate) {
return;
@@ -230,10 +244,14 @@ export class SavedObjectSaveModal extends React.Component {
objectType: this.props.objectType,
confirmSaveLabel: (
-
+ {this.props.confirmButtonLabel
+ ? this.props.confirmButtonLabel
+ : i18n.translate(
+ 'kibana-react.savedObjects.saveModal.duplicateTitleDescription.confirmSaveText',
+ {
+ defaultMessage: 'Confirm save',
+ }
+ )}
),
}}
diff --git a/src/test_utils/public/enzyme_helpers.tsx b/src/test_utils/public/enzyme_helpers.tsx
index 17027bd8dce48..43ec49c5c1404 100644
--- a/src/test_utils/public/enzyme_helpers.tsx
+++ b/src/test_utils/public/enzyme_helpers.tsx
@@ -128,3 +128,15 @@ export function renderWithIntl(
}
export const nextTick = () => new Promise(res => process.nextTick(res));
+
+export function shallowWithI18nProvider(child: ReactElement) {
+ const wrapped = shallow({child});
+ const name = typeof child.type === 'string' ? child.type : child.type.name;
+ return wrapped.find(name).dive();
+}
+
+export function mountWithI18nProvider(child: ReactElement) {
+ const wrapped = mount({child});
+ const name = typeof child.type === 'string' ? child.type : child.type.name;
+ return wrapped.find(name);
+}
diff --git a/test/functional/apps/dashboard/dashboard_filter_bar.js b/test/functional/apps/dashboard/dashboard_filter_bar.js
index 312066000c63c..a48393e11b19c 100644
--- a/test/functional/apps/dashboard/dashboard_filter_bar.js
+++ b/test/functional/apps/dashboard/dashboard_filter_bar.js
@@ -133,10 +133,10 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.dashboard.setTimepickerInDataRange();
});
- it('are added when pie chart legend item is clicked', async function () {
- await dashboardAddPanel.addVisualization('Rendering Test: pie');
+ it('are added when a cell magnifying glass is clicked', async function () {
+ await dashboardAddPanel.addSavedSearch('Rendering-Test:-saved-search');
await PageObjects.dashboard.waitForRenderComplete();
- await pieChart.filterByLegendItem('4,886');
+ await testSubjects.click('docTableCellFilter');
const filterCount = await filterBar.getFilterCount();
expect(filterCount).to.equal(1);
diff --git a/test/functional/apps/dashboard/dashboard_filtering.js b/test/functional/apps/dashboard/dashboard_filtering.js
index 15b444cb74151..862ed8c87d347 100644
--- a/test/functional/apps/dashboard/dashboard_filtering.js
+++ b/test/functional/apps/dashboard/dashboard_filtering.js
@@ -74,7 +74,6 @@ export default function ({ getService, getPageObjects }) {
it('tsvb time series shows no data message', async () => {
expect(await testSubjects.exists('noTSVBDataMessage')).to.be(true);
- await dashboardExpect.tsvbTimeSeriesLegendCount(0);
});
it('metric value shows no data', async () => {
@@ -134,11 +133,6 @@ export default function ({ getService, getPageObjects }) {
await dashboardExpect.goalAndGuageLabelsExist(['0', '0%']);
});
- it('tsvb time series shows no data message', async () => {
- expect(await testSubjects.exists('noTSVBDataMessage')).to.be(true);
- await dashboardExpect.tsvbTimeSeriesLegendCount(0);
- });
-
it('metric value shows no data', async () => {
await dashboardExpect.metricValuesExist(['-']);
});
@@ -195,11 +189,6 @@ export default function ({ getService, getPageObjects }) {
await dashboardExpect.goalAndGuageLabelsExist(['39.958%', '7,544']);
});
- it('tsvb time series', async () => {
- expect(await testSubjects.exists('noTSVBDataMessage')).to.be(false);
- await dashboardExpect.tsvbTimeSeriesLegendCount(10);
- });
-
it('metric value', async () => {
await dashboardExpect.metricValuesExist(['101']);
});
diff --git a/test/functional/apps/dashboard/dashboard_grid.js b/test/functional/apps/dashboard/dashboard_grid.js
index 958d61176ded2..a0c22ca85b91b 100644
--- a/test/functional/apps/dashboard/dashboard_grid.js
+++ b/test/functional/apps/dashboard/dashboard_grid.js
@@ -25,7 +25,6 @@ export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['dashboard']);
describe('dashboard grid', function () {
- this.tags(['skipFirefox']);
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
diff --git a/test/functional/apps/dashboard/embeddable_rendering.js b/test/functional/apps/dashboard/embeddable_rendering.js
index 831622716f381..e21964495e46e 100644
--- a/test/functional/apps/dashboard/embeddable_rendering.js
+++ b/test/functional/apps/dashboard/embeddable_rendering.js
@@ -50,7 +50,6 @@ export default function ({ getService, getPageObjects }) {
await dashboardExpect.tagCloudWithValuesFound(['CN', 'IN', 'US', 'BR', 'ID']);
// TODO add test for 'region map viz'
// TODO add test for 'tsvb gauge' viz
- await dashboardExpect.tsvbTimeSeriesLegendCount(1);
// TODO add test for 'geo map' viz
// This tests the presence of the two input control embeddables
await dashboardExpect.inputControlItemCount(5);
@@ -86,7 +85,6 @@ export default function ({ getService, getPageObjects }) {
await dashboardExpect.tsvbMetricValuesExist(['0']);
await dashboardExpect.tsvbMarkdownWithValuesExists(['Hi Avg last bytes: 0']);
await dashboardExpect.tsvbTableCellCount(0);
- await dashboardExpect.tsvbTimeSeriesLegendCount(1);
await dashboardExpect.tsvbTopNValuesExist(['0']);
await dashboardExpect.vegaTextsDoNotExist(['5,000']);
};
diff --git a/test/functional/apps/dashboard/full_screen_mode.js b/test/functional/apps/dashboard/full_screen_mode.js
index 1f935e9b9aa1b..2a534a66d43e4 100644
--- a/test/functional/apps/dashboard/full_screen_mode.js
+++ b/test/functional/apps/dashboard/full_screen_mode.js
@@ -21,7 +21,6 @@ import expect from '@kbn/expect';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
- const browser = getService('browser');
const dashboardPanelActions = getService('dashboardPanelActions');
const PageObjects = getPageObjects(['dashboard', 'common']);
@@ -69,7 +68,7 @@ export default function ({ getService, getPageObjects }) {
it('exits when the text button is clicked on', async () => {
const logoButton = await PageObjects.dashboard.getExitFullScreenLogoButton();
- await browser.moveMouseTo(logoButton);
+ await logoButton.moveMouseTo();
await PageObjects.dashboard.clickExitFullScreenTextButton();
await retry.try(async () => {
diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js
index 3cb5d0a71cce5..c29542e6a4035 100644
--- a/test/functional/apps/discover/_discover.js
+++ b/test/functional/apps/discover/_discover.js
@@ -160,7 +160,7 @@ export default function ({ getService, getPageObjects }) {
const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
expect(Math.round(newDurationHours)).to.be(3);
- const rowData = await PageObjects.discover.getDocTableIndex(1);
+ const rowData = await PageObjects.discover.getDocTableField(1);
expect(rowData).to.have.string('Sep 20, 2015 @ 02:56:02.323');
});
diff --git a/test/functional/apps/management/index.js b/test/functional/apps/management/index.js
index c9b444e501789..4d4031b4e489b 100644
--- a/test/functional/apps/management/index.js
+++ b/test/functional/apps/management/index.js
@@ -33,7 +33,7 @@ export default function ({ getService, loadTestFile }) {
});
describe('', function () {
- this.tags('ciGroup1');
+ this.tags('ciGroup7');
loadTestFile(require.resolve('./_create_index_pattern_wizard'));
loadTestFile(require.resolve('./_index_pattern_create_delete'));
@@ -45,7 +45,7 @@ export default function ({ getService, loadTestFile }) {
});
describe('', function () {
- this.tags('ciGroup2');
+ this.tags('ciGroup8');
loadTestFile(require.resolve('./_index_pattern_filter'));
loadTestFile(require.resolve('./_scripted_fields_filter'));
diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts
index 0a2400a367a76..fa79190a5bf94 100644
--- a/test/functional/apps/visualize/_tsvb_time_series.ts
+++ b/test/functional/apps/visualize/_tsvb_time_series.ts
@@ -75,7 +75,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
await visualBuilder.changePanelPreview();
await visualBuilder.cloneSeries();
- const legend = await visualBuilder.getLegentItems();
+ const legend = await visualBuilder.getLegendItems();
const series = await visualBuilder.getSeries();
expect(legend.length).to.be(2);
expect(series.length).to.be(2);
@@ -108,8 +108,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
expect(actualCount).to.be(expectedLegendValue);
});
- // FLAKY: https://github.com/elastic/kibana/issues/40458
- it('should show the correct count in the legend with "Human readable" duration formatter', async () => {
+ it.skip('should show the correct count in the legend with "Human readable" duration formatter', async () => {
await visualBuilder.clickSeriesOption();
await visualBuilder.changeDataFormatter('Duration');
await visualBuilder.setDurationFormatterSettings({ to: 'Human readable' });
@@ -127,7 +126,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
expect(actualCountMin).to.be('3 hours');
});
- describe('Dark mode', () => {
+ // --reversed class is not implemented in @elastic\chart
+ describe.skip('Dark mode', () => {
before(async () => {
await kibanaServer.uiSettings.update({
'theme:darkMode': true,
diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts
index 68a00b29d107e..2a13b6fea9158 100644
--- a/test/functional/apps/visualize/index.ts
+++ b/test/functional/apps/visualize/index.ts
@@ -40,7 +40,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) {
});
describe('', function() {
- this.tags('ciGroup3');
+ this.tags('ciGroup9');
loadTestFile(require.resolve('./_embedding_chart'));
loadTestFile(require.resolve('./_chart_types'));
@@ -50,7 +50,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) {
});
describe('', function() {
- this.tags('ciGroup4');
+ this.tags('ciGroup10');
loadTestFile(require.resolve('./_inspector'));
loadTestFile(require.resolve('./_experimental_vis'));
@@ -62,7 +62,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) {
});
describe('', function() {
- this.tags('ciGroup5');
+ this.tags('ciGroup11');
loadTestFile(require.resolve('./_line_chart'));
loadTestFile(require.resolve('./_pie_chart'));
@@ -76,7 +76,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) {
});
describe('', function() {
- this.tags('ciGroup6');
+ this.tags('ciGroup12');
loadTestFile(require.resolve('./_tag_cloud'));
loadTestFile(require.resolve('./_tile_map'));
diff --git a/test/functional/page_objects/common_page.js b/test/functional/page_objects/common_page.js
index 651d82608961a..fc47a53a092e8 100644
--- a/test/functional/page_objects/common_page.js
+++ b/test/functional/page_objects/common_page.js
@@ -151,11 +151,22 @@ export function CommonPageProvider({ getService, getPageObjects }) {
navigateToApp(appName, { basePath = '', shouldLoginIfPrompted = true, shouldAcceptAlert = true, hash = '' } = {}) {
const self = this;
- const appConfig = config.get(['apps', appName]);
- const appUrl = getUrl.noAuth(config.get('servers.kibana'), {
- pathname: `${basePath}${appConfig.pathname}`,
- hash: hash || appConfig.hash,
- });
+
+ let appUrl;
+ if (config.has(['apps', appName])) {
+ // Legacy applications
+ const appConfig = config.get(['apps', appName]);
+ appUrl = getUrl.noAuth(config.get('servers.kibana'), {
+ pathname: `${basePath}${appConfig.pathname}`,
+ hash: hash || appConfig.hash,
+ });
+ } else {
+ appUrl = getUrl.noAuth(config.get('servers.kibana'), {
+ pathname: `${basePath}/app/${appName}`,
+ hash
+ });
+ }
+
log.debug('navigating to ' + appName + ' url: ' + appUrl);
function navigateTo(url) {
@@ -359,7 +370,7 @@ export function CommonPageProvider({ getService, getPageObjects }) {
throw new Error('Toast is not visible yet');
}
});
- await browser.moveMouseTo(toast);
+ await toast.moveMouseTo();
const title = await (await find.byCssSelector('.euiToastHeader__title')).getVisibleText();
log.debug(title);
await find.clickByCssSelector('.euiToast__closeButton');
@@ -370,7 +381,7 @@ export function CommonPageProvider({ getService, getPageObjects }) {
const toasts = await find.allByCssSelector('.euiToast');
for (const toastElement of toasts) {
try {
- await browser.moveMouseTo(toastElement);
+ await toastElement.moveMouseTo();
const closeBtn = await toastElement.findByCssSelector('.euiToast__closeButton');
await closeBtn.click();
} catch (err) {
diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js
index 6954bed438478..dfe8dc1071d46 100644
--- a/test/functional/page_objects/discover_page.js
+++ b/test/functional/page_objects/discover_page.js
@@ -193,6 +193,13 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
return await row.getVisibleText();
}
+ async getDocTableField(index) {
+ const field = await find.byCssSelector(
+ `tr.kbnDocTable__row:nth-child(${index}) > [data-test-subj='docTableField']`
+ );
+ return await field.getVisibleText();
+ }
+
async clickDocSortDown() {
await find.clickByCssSelector('.fa-sort-down');
}
diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts
index c64d623979313..5f34e5c4f8637 100644
--- a/test/functional/page_objects/visual_builder_page.ts
+++ b/test/functional/page_objects/visual_builder_page.ts
@@ -67,11 +67,14 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
}
public async checkTimeSeriesChartIsPresent() {
- await testSubjects.existOrFail('timeseriesChart');
+ const isPresent = await find.existsByCssSelector('.tvbVisTimeSeries');
+ if (!isPresent) {
+ throw new Error(`TimeSeries chart is not loaded`);
+ }
}
public async checkTimeSeriesLegendIsPresent() {
- const isPresent = await find.existsByCssSelector('.tvbLegend');
+ const isPresent = await find.existsByCssSelector('.echLegend');
if (!isPresent) {
throw new Error(`TimeSeries legend is not loaded`);
}
@@ -239,7 +242,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
formatter: 'Bytes' | 'Number' | 'Percent' | 'Duration' | 'Custom'
) {
const formatterEl = await find.byCssSelector('[id$="row"] .euiComboBox');
- await comboBox.setElement(formatterEl, formatter);
+ await comboBox.setElement(formatterEl, formatter, { clickWithMouse: true });
}
/**
@@ -260,11 +263,11 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
}) {
if (from) {
const fromCombobox = await find.byCssSelector('[id$="from-row"] .euiComboBox');
- await comboBox.setElement(fromCombobox, from);
+ await comboBox.setElement(fromCombobox, from, { clickWithMouse: true });
}
if (to) {
const toCombobox = await find.byCssSelector('[id$="to-row"] .euiComboBox');
- await comboBox.setElement(toCombobox, to);
+ await comboBox.setElement(toCombobox, to, { clickWithMouse: true });
}
if (decimalPlaces) {
const decimalPlacesInput = await find.byCssSelector('[id$="decimal"]');
@@ -291,9 +294,11 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
await el.type(value);
}
- public async getRhythmChartLegendValue() {
+ public async getRhythmChartLegendValue(nth = 0) {
await PageObjects.visualize.waitForVisualizationRenderingStabilized();
- const metricValue = await find.byCssSelector('.tvbLegend__itemValue');
+ const metricValue = (await find.allByCssSelector(
+ `.echLegendItem .echLegendItem__displayValue`
+ ))[nth];
await metricValue.moveMouseTo();
return await metricValue.getVisibleText();
}
@@ -447,7 +452,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
public async clickColorPicker(): Promise {
const picker = await find.byCssSelector('.tvbColorPicker button');
- await browser.clickMouseButton(picker);
+ await picker.clickMouseButton();
}
public async setBackgroundColor(colorHex: string): Promise {
@@ -502,8 +507,8 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
await PageObjects.visualize.waitForRenderingCount(prevRenderingCount + 1);
}
- public async getLegentItems(): Promise {
- return await testSubjects.findAll('tsvbLegendItem');
+ public async getLegendItems(): Promise {
+ return await find.allByCssSelector('.echLegendItem');
}
public async getSeries(): Promise {
diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js
index db6656deed7cb..d02638d37aa49 100644
--- a/test/functional/page_objects/visualize_page.js
+++ b/test/functional/page_objects/visualize_page.js
@@ -1187,7 +1187,7 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli
await retry.try(async () => {
const table = await testSubjects.find('tableVis');
const cell = await table.findByCssSelector(`tbody tr:nth-child(${row}) td:nth-child(${column})`);
- await browser.moveMouseTo(cell);
+ await cell.moveMouseTo();
const filterBtn = await testSubjects.findDescendant('filterForCellValue', cell);
await filterBtn.click();
});
diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts
index d52be4b90c043..ccd32590e941c 100644
--- a/test/functional/services/browser.ts
+++ b/test/functional/services/browser.ts
@@ -155,49 +155,26 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
}
/**
- * Moves the remote environment’s mouse cursor to the specified element or relative
- * position.
+ * Moves the remote environment’s mouse cursor to the specified point {x, y} which is
+ * offset to browser page top left corner.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#move
*
- * @param {WebElementWrapper} element Optional
- * @param {number} xOffset Optional
- * @param {number} yOffset Optional
+ * @param {x: number, y: number} point on browser page
* @return {Promise}
*/
- public async moveMouseTo(element: any, xOffset: number, yOffset: number): Promise;
- public async moveMouseTo(element: WebElementWrapper): Promise;
- public async moveMouseTo(
- element: WebElementWrapper,
- xOffset?: number,
- yOffset?: number
- ): Promise {
+ public async moveMouseTo(point: { x: number; y: number }): Promise {
if (this.isW3CEnabled) {
- // Workaround for scrolling bug in W3C mode: move pointer to { x: 0, y: 0 }
- // https://github.com/mozilla/geckodriver/issues/776
await this.getActions()
.move({ x: 0, y: 0 })
.perform();
- if (element instanceof WebElementWrapper) {
- await this.getActions()
- .move({ x: xOffset || 10, y: yOffset || 10, origin: element._webElement })
- .perform();
- } else {
- await this.getActions()
- .move({ origin: { x: xOffset, y: yOffset } })
- .perform();
- }
+ await this.getActions()
+ .move({ x: point.x, y: point.y, origin: 'pointer' })
+ .perform();
} else {
- if (element instanceof WebElementWrapper) {
- await this.getActions()
- .pause(this.getActions().mouse)
- .move({ origin: element._webElement })
- .perform();
- } else {
- await this.getActions()
- .pause(this.getActions().mouse)
- .move({ origin: { x: xOffset, y: yOffset } })
- .perform();
- }
+ await this.getActions()
+ .pause(this.getActions().mouse)
+ .move({ x: point.x, y: point.y, origin: 'pointer' })
+ .perform();
}
}
@@ -213,70 +190,47 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
from: { offset: { x: any; y: any }; location: any },
to: { offset: { x: any; y: any }; location: any }
) {
- // tslint:disable-next-line:variable-name
- let _from;
- // tslint:disable-next-line:variable-name
- let _to;
- // tslint:disable-next-line:variable-name
- const _fromOffset = from.offset
- ? { x: from.offset.x || 0, y: from.offset.y || 0 }
- : { x: 0, y: 0 };
- // tslint:disable-next-line:variable-name
- const _toOffset = to.offset ? { x: to.offset.x || 0, y: to.offset.y || 0 } : { x: 0, y: 0 };
- // tslint:disable-next-line:variable-name
- const _convertPointW3C = async (point: any, offset: { x: any; y: any }) => {
- if (point.location instanceof WebElementWrapper) {
- const position = await point.location.getPosition();
- return {
- x: Math.round(position.x + offset.x),
- y: Math.round(position.y + offset.y),
- };
- } else {
- return {
- x: Math.round(point.location.x + offset.x),
- y: Math.round(point.location.y + offset.y),
- };
- }
- };
- // tslint:disable-next-line:variable-name
- const _convertPoint = (point: any) => {
- return point.location instanceof WebElementWrapper
- ? point.location._webElement
- : point.location;
- };
-
if (this.isW3CEnabled) {
- // tslint:disable-next-line:variable-name
- _from = await _convertPointW3C(from, _fromOffset);
- // tslint:disable-next-line:variable-name
- _to = await _convertPointW3C(to, _toOffset);
- // tslint:disable-next-line:variable-name
- const _offset = { x: _to.x - _from.x, y: _to.y - _from.y };
+ // The offset should be specified in pixels relative to the center of the element's bounding box
+ const getW3CPoint = (data: any) => {
+ if (!data.offset) {
+ data.offset = {};
+ }
+ return data.location instanceof WebElementWrapper
+ ? { x: data.offset.x || 0, y: data.offset.y || 0, origin: data.location._webElement }
+ : { x: data.location.x, y: data.location.y, origin: 'pointer' };
+ };
+ const startPoint = getW3CPoint(from);
+ const endPoint = getW3CPoint(to);
+ await this.getActions()
+ .move({ x: 0, y: 0 })
+ .perform();
return await this.getActions()
- .move({ x: _from.x, y: _from.y, origin: 'pointer' })
+ .move(startPoint)
.press()
- .move({ x: _offset.x, y: _offset.y, origin: 'pointer' })
+ .move(endPoint)
.release()
.perform();
} else {
- // until Chromedriver is not supporting W3C Webdriver Actions API
- // tslint:disable-next-line:variable-name
- _from = _convertPoint(from);
- // tslint:disable-next-line:variable-name
- _to = _convertPoint(to);
- if (from.location instanceof WebElementWrapper && typeof to.location.x === 'number') {
+ // The offset should be specified in pixels relative to the top-left corner of the element's bounding box
+ const getOffset: any = (offset: { x: number; y: number }) =>
+ offset ? { x: offset.x || 0, y: offset.y || 0 } : { x: 0, y: 0 };
+
+ if (from.location instanceof WebElementWrapper === false) {
+ throw new Error('Dragging point should be WebElementWrapper instance');
+ } else if (typeof to.location.x === 'number') {
return await this.getActions()
- .move({ origin: _from })
+ .move({ origin: from.location._webElement })
.press()
- .move({ x: _to.x, y: _to.y, origin: 'pointer' })
+ .move({ x: to.location.x, y: to.location.y, origin: 'pointer' })
.release()
.perform();
} else {
return await new LegacyActionSequence(driver)
- .mouseMove(_from, _fromOffset)
+ .mouseMove(from.location._webElement, getOffset(from.offset))
.mouseDown()
- .mouseMove(_to, _toOffset)
+ .mouseMove(to.location._webElement, getOffset(to.offset))
.mouseUp()
.perform();
}
@@ -320,34 +274,29 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
}
/**
- * Inserts an action for moving the mouse x and y pixels relative to the specified origin.
- * The origin may be defined as the mouse's current position, the viewport, or the center
- * of a specific WebElement. Then adds an action for left-click (down/up) with the mouse.
+ * Moves the remote environment’s mouse cursor to the specified point {x, y} which is
+ * offset to browser page top left corner.
+ * Then adds an action for left-click (down/up) with the mouse.
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#click
*
- * @param {WebElementWrapper} element Optional
- * @param {number} xOffset Optional
- * @param {number} yOffset Optional
+ * @param {x: number, y: number} point on browser page
* @return {Promise}
*/
- public async clickMouseButton(element: any, xOffset: number, yOffset: number): Promise;
- public async clickMouseButton(element: WebElementWrapper): Promise;
- public async clickMouseButton(...args: unknown[]): Promise {
- const arg0 = args[0];
- if (arg0 instanceof WebElementWrapper) {
+ public async clickMouseButton(point: { x: number; y: number }): Promise {
+ if (this.isW3CEnabled) {
await this.getActions()
- .pause(this.getActions().mouse)
- .move({ origin: arg0._webElement })
+ .move({ x: 0, y: 0 })
+ .perform();
+ await this.getActions()
+ .move({ x: point.x, y: point.y, origin: 'pointer' })
.click()
.perform();
- } else if (isNaN(args[1] as number) || isNaN(args[2] as number) === false) {
+ } else {
await this.getActions()
.pause(this.getActions().mouse)
- .move({ origin: { x: args[1], y: args[2] } })
+ .move({ x: point.x, y: point.y, origin: 'pointer' })
.click()
.perform();
- } else {
- throw new Error('Element or coordinates should be provided');
}
}
@@ -378,16 +327,10 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
* @param {WebElementWrapper} element
* @return {Promise}
*/
- public async doubleClick(element?: WebElementWrapper): Promise {
- if (element instanceof WebElementWrapper) {
- await this.getActions()
- .doubleClick(element._webElement)
- .perform();
- } else {
- await this.getActions()
- .doubleClick()
- .perform();
- }
+ public async doubleClick(): Promise {
+ await this.getActions()
+ .doubleClick()
+ .perform();
}
/**
diff --git a/test/functional/services/combo_box.ts b/test/functional/services/combo_box.ts
index b8267f0b4cbe3..5ecd8cd883c8a 100644
--- a/test/functional/services/combo_box.ts
+++ b/test/functional/services/combo_box.ts
@@ -46,13 +46,21 @@ export function ComboBoxProvider({ getService, getPageObjects }: FtrProviderCont
await this.setElement(comboBox, value);
}
+ private async clickOption(isMouseClick: boolean, element: WebElementWrapper) {
+ return isMouseClick ? await element.clickMouseButton() : await element.click();
+ }
+
/**
* set value inside combobox element
*
* @param comboBoxElement
* @param value
*/
- public async setElement(comboBoxElement: WebElementWrapper, value: string): Promise {
+ public async setElement(
+ comboBoxElement: WebElementWrapper,
+ value: string,
+ options = { clickWithMouse: false }
+ ): Promise {
log.debug(`comboBox.setElement, value: ${value}`);
const isOptionSelected = await this.isOptionSelected(comboBoxElement, value);
@@ -65,21 +73,22 @@ export function ComboBoxProvider({ getService, getPageObjects }: FtrProviderCont
await this.openOptionsList(comboBoxElement);
if (value !== undefined) {
- const options = await find.allByCssSelector(
+ const selectOptions = await find.allByCssSelector(
`.euiFilterSelectItem[title^="${value.toString().trim()}"]`,
WAIT_FOR_EXISTS_TIME
);
- if (options.length > 0) {
- await options[0].click();
+ if (selectOptions.length > 0) {
+ await this.clickOption(options.clickWithMouse, selectOptions[0]);
} else {
// if it doesn't find the item which text starts with value, it will choose the first option
- await find.clickByCssSelector('.euiFilterSelectItem');
+ const firstOption = await find.byCssSelector('.euiFilterSelectItem');
+ await this.clickOption(options.clickWithMouse, firstOption);
}
} else {
- await find.clickByCssSelector('.euiFilterSelectItem');
+ const firstOption = await find.byCssSelector('.euiFilterSelectItem');
+ await this.clickOption(options.clickWithMouse, firstOption);
}
-
await this.closeOptionsList(comboBoxElement);
}
@@ -241,11 +250,11 @@ export function ComboBoxProvider({ getService, getPageObjects }: FtrProviderCont
value: string
): Promise {
log.debug(`comboBox.isOptionSelected, value: ${value}`);
- const selectedOptions = await comboBoxElement.findAllByClassName(
- 'euiComboBoxPill',
- WAIT_FOR_EXISTS_TIME
- );
- return selectedOptions.length === 1 && (await selectedOptions[0].getVisibleText()) === value;
+ const $ = await comboBoxElement.parseDomContent();
+ const selectedOptions = $('.euiComboBoxPill')
+ .toArray()
+ .map(option => $(option).text());
+ return selectedOptions.length === 1 && selectedOptions[0] === value;
}
}
diff --git a/test/functional/services/dashboard/expectations.js b/test/functional/services/dashboard/expectations.js
index b6bede32b769c..abafe89c40941 100644
--- a/test/functional/services/dashboard/expectations.js
+++ b/test/functional/services/dashboard/expectations.js
@@ -62,14 +62,6 @@ export function DashboardExpectProvider({ getService, getPageObjects }) {
});
}
- async tsvbTimeSeriesLegendCount(expectedCount) {
- log.debug(`DashboardExpect.tsvbTimeSeriesLegendCount(${expectedCount})`);
- await retry.try(async () => {
- const tsvbLegendItems = await testSubjects.findAll('tsvbLegendItem', findTimeout);
- expect(tsvbLegendItems.length).to.be(expectedCount);
- });
- }
-
async fieldSuggestions(expectedFields) {
log.debug(`DashboardExpect.fieldSuggestions(${expectedFields})`);
const fields = await filterBar.getFilterEditorFields();
diff --git a/test/functional/services/dashboard/panel_actions.js b/test/functional/services/dashboard/panel_actions.js
index 051074eb9b3d0..b7327f4af6d17 100644
--- a/test/functional/services/dashboard/panel_actions.js
+++ b/test/functional/services/dashboard/panel_actions.js
@@ -26,7 +26,6 @@ const OPEN_INSPECTOR_TEST_SUBJ = 'embeddablePanelAction-openInspector';
export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
const log = getService('log');
- const browser = getService('browser');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['header', 'common']);
@@ -45,7 +44,7 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
async toggleContextMenu(parent) {
log.debug('toggleContextMenu');
- await (parent ? browser.moveMouseTo(parent) : testSubjects.moveMouseTo('dashboardPanelTitle'));
+ await (parent ? parent.moveMouseTo() : testSubjects.moveMouseTo('dashboardPanelTitle'));
const toggleMenuItem = await this.findContextMenu(parent);
await toggleMenuItem.click();
}
diff --git a/test/functional/services/inspector.js b/test/functional/services/inspector.js
index 9c25ebea48b4f..d7c3109251aaf 100644
--- a/test/functional/services/inspector.js
+++ b/test/functional/services/inspector.js
@@ -22,7 +22,6 @@ import expect from '@kbn/expect';
export function InspectorProvider({ getService }) {
const log = getService('log');
const retry = getService('retry');
- const browser = getService('browser');
const renderable = getService('renderable');
const flyout = getService('flyout');
const testSubjects = getService('testSubjects');
@@ -132,7 +131,7 @@ export function InspectorProvider({ getService }) {
await retry.try(async () => {
const table = await testSubjects.find('inspectorTable');
const cell = await table.findByCssSelector(`tbody tr:nth-child(${row}) td:nth-child(${column})`);
- await browser.moveMouseTo(cell);
+ await cell.moveMouseTo();
const filterBtn = await testSubjects.findDescendant('filterForInspectorCellValue', cell);
await filterBtn.click();
});
@@ -143,7 +142,7 @@ export function InspectorProvider({ getService }) {
await retry.try(async () => {
const table = await testSubjects.find('inspectorTable');
const cell = await table.findByCssSelector(`tbody tr:nth-child(${row}) td:nth-child(${column})`);
- await browser.moveMouseTo(cell);
+ await cell.moveMouseTo();
const filterBtn = await testSubjects.findDescendant('filterOutInspectorCellValue', cell);
await filterBtn.click();
});
diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
index b05485618da01..65478c4e5ccd2 100644
--- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
+++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
@@ -58,6 +58,7 @@ export class WebElementWrapper {
private Keys: IKey = this.webDriver.Key;
private driver: WebDriver = this.webDriver.driver;
public LegacyAction: any = this.webDriver.LegacyActionSequence;
+ public isW3CEnabled: boolean = (this.webDriver.driver as any).executor_.w3c === true;
public static create(
webElement: WebElement | WebElementWrapper,
@@ -149,6 +150,12 @@ export class WebElementWrapper {
}
}
+ private getActions(): any {
+ return this.isW3CEnabled
+ ? (this.driver as any).actions()
+ : (this.driver as any).actions({ bridge: true });
+ }
+
/**
* Returns whether or not the element would be visible to an actual user. This means
* that the following types of elements are considered to be not displayed:
@@ -402,29 +409,81 @@ export class WebElementWrapper {
}
/**
- * Moves the remote environment’s mouse cursor to the current element
+ * Moves the remote environment’s mouse cursor to the current element with optional offset
* https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#move
- *
+ * @param { xOffset: 0, yOffset: 0 } options
* @return {Promise}
*/
- public async moveMouseTo() {
+ public async moveMouseTo(options = { xOffset: 0, yOffset: 0 }) {
await this.retryCall(async function moveMouseTo(wrapper) {
await wrapper.scrollIntoViewIfNecessary();
- if (wrapper.browserType === Browsers.Firefox) {
- const actions = (wrapper.driver as any).actions();
- await actions.move({ x: 0, y: 0 }).perform();
- await actions.move({ x: 10, y: 10, origin: wrapper._webElement }).perform();
+ if (wrapper.isW3CEnabled) {
+ await wrapper
+ .getActions()
+ .move({ x: 0, y: 0 })
+ .perform();
+ await wrapper
+ .getActions()
+ .move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
+ .perform();
} else {
- const mouse = (wrapper.driver.actions() as any).mouse();
- const actions = (wrapper.driver as any).actions({ bridge: true });
- await actions
- .pause(mouse)
- .move({ origin: wrapper._webElement })
+ await wrapper
+ .getActions()
+ .pause(wrapper.getActions().mouse)
+ .move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
.perform();
}
});
}
+ /**
+ * Inserts an action for moving the mouse to element center, unless optional offset is provided.
+ * Then adds an action for left-click (down/up) with the mouse.
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#click
+ *
+ * @param { xOffset: 0, yOffset: 0 } options Optional
+ * @return {Promise}
+ */
+ public async clickMouseButton(options = { xOffset: 0, yOffset: 0 }): Promise {
+ await this.retryCall(async function clickMouseButton(wrapper) {
+ await wrapper.scrollIntoViewIfNecessary();
+ if (wrapper.isW3CEnabled) {
+ await wrapper
+ .getActions()
+ .move({ x: 0, y: 0 })
+ .perform();
+ await wrapper
+ .getActions()
+ .move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
+ .click()
+ .perform();
+ } else {
+ await wrapper
+ .getActions()
+ .pause(wrapper.getActions().mouse)
+ .move({ x: options.xOffset, y: options.yOffset, origin: wrapper._webElement })
+ .click()
+ .perform();
+ }
+ });
+ }
+
+ /**
+ * Inserts action for performing a double left-click with the mouse.
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#doubleClick
+ * @param {WebElementWrapper} element
+ * @return {Promise