diff --git a/src/legacy/core_plugins/table_vis/index.js b/src/legacy/core_plugins/table_vis/index.ts similarity index 54% rename from src/legacy/core_plugins/table_vis/index.js rename to src/legacy/core_plugins/table_vis/index.ts index affdcae73df70..2580932f03132 100644 --- a/src/legacy/core_plugins/table_vis/index.js +++ b/src/legacy/core_plugins/table_vis/index.ts @@ -18,17 +18,27 @@ */ import { resolve } from 'path'; +import { Legacy } from 'kibana'; -export default function (kibana) { +import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - return new kibana.Plugin({ +const tableVisPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => + new Plugin({ + id: 'table_vis', + require: ['kibana', 'elasticsearch', 'visualizations', 'interpreter', 'data'], + publicDir: resolve(__dirname, 'public'), uiExports: { - visTypes: [ - 'plugins/table_vis/table_vis' - ], - interpreter: ['plugins/table_vis/table_vis_fn'], styleSheetPaths: resolve(__dirname, 'public/index.scss'), + hacks: [resolve(__dirname, 'public/legacy')], + injectDefaultVars: server => ({}), }, - }); + init: (server: Legacy.Server) => ({}), + config(Joi: any) { + return Joi.object({ + enabled: Joi.boolean().default(true), + }).default(); + }, + } as Legacy.PluginSpecOptions); -} +// eslint-disable-next-line import/no-default-export +export default tableVisPluginInitializer; diff --git a/src/legacy/core_plugins/table_vis/public/__tests__/_table_vis_controller.js b/src/legacy/core_plugins/table_vis/public/__tests__/table_vis_controller.js similarity index 91% rename from src/legacy/core_plugins/table_vis/public/__tests__/_table_vis_controller.js rename to src/legacy/core_plugins/table_vis/public/__tests__/table_vis_controller.js index a1b5ebbe8edca..c8f8194f7f98a 100644 --- a/src/legacy/core_plugins/table_vis/public/__tests__/_table_vis_controller.js +++ b/src/legacy/core_plugins/table_vis/public/__tests__/table_vis_controller.js @@ -22,11 +22,15 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy'; import { VisProvider } from 'ui/vis'; +import { VisFactoryProvider } from 'ui/vis/vis_factory'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { AppStateProvider } from 'ui/state_management/app_state'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; -describe('Table Vis Controller', function () { +import { createTableVisTypeDefinition } from '../table_vis_type'; +import { visualizations } from '../../../visualizations/public'; + +describe('Table Vis - Controller', async function () { let $rootScope; let $compile; let Private; @@ -37,10 +41,20 @@ describe('Table Vis Controller', function () { let AppState; let tableAggResponse; let tabifiedResponse; + let legacyDependencies; beforeEach(ngMock.module('kibana', 'kibana/table_vis')); beforeEach(ngMock.inject(function ($injector) { Private = $injector.get('Private'); + legacyDependencies = { + // eslint-disable-next-line new-cap + createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization + }; + + visualizations.types.VisTypesRegistryProvider.register(() => + createTableVisTypeDefinition(legacyDependencies) + ); + $rootScope = $injector.get('$rootScope'); $compile = $injector.get('$compile'); fixtures = require('fixtures/fake_hierarchical_data'); diff --git a/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/_table.js b/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/agg_table.js similarity index 96% rename from src/legacy/core_plugins/table_vis/public/agg_table/__tests__/_table.js rename to src/legacy/core_plugins/table_vis/public/agg_table/__tests__/agg_table.js index 8375e1fe6df5d..5c2940ff40efe 100644 --- a/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/_table.js +++ b/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/agg_table.js @@ -29,7 +29,11 @@ import { VisProvider } from 'ui/vis'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; import { round } from 'lodash'; -describe('AggTable Directive', function () { +import { VisFactoryProvider } from 'ui/vis/vis_factory'; +import { createTableVisTypeDefinition } from '../../table_vis_type'; +import { visualizations } from '../../../../visualizations/public'; + +describe('Table Vis - AggTable Directive', function () { let $rootScope; let $compile; @@ -37,6 +41,7 @@ describe('AggTable Directive', function () { let indexPattern; let settings; let tableAggResponse; + let legacyDependencies; const tabifiedData = {}; const init = () => { @@ -82,6 +87,15 @@ describe('AggTable Directive', function () { beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function ($injector, Private, config) { + legacyDependencies = { + // eslint-disable-next-line new-cap + createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization + }; + + visualizations.types.VisTypesRegistryProvider.register(() => + createTableVisTypeDefinition(legacyDependencies) + ); + tableAggResponse = legacyResponseHandlerProvider().handler; indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); Vis = Private(VisProvider); diff --git a/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/_group.js b/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/agg_table_group.js similarity index 88% rename from src/legacy/core_plugins/table_vis/public/agg_table/__tests__/_group.js rename to src/legacy/core_plugins/table_vis/public/agg_table/__tests__/agg_table_group.js index 919ccbfbb4ef5..075a07f0c96b2 100644 --- a/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/_group.js +++ b/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/agg_table_group.js @@ -26,13 +26,17 @@ import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logsta import { VisProvider } from 'ui/vis'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; -describe('AggTableGroup Directive', function () { +import { VisFactoryProvider } from 'ui/vis/vis_factory'; +import { createTableVisTypeDefinition } from '../../table_vis_type'; +import { visualizations } from '../../../../visualizations/public'; +describe('Table Vis - AggTableGroup Directive', function () { let $rootScope; let $compile; let Vis; let indexPattern; let tableAggResponse; + let legacyDependencies; const tabifiedData = {}; const init = () => { @@ -56,6 +60,15 @@ describe('AggTableGroup Directive', function () { beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function ($injector, Private) { + legacyDependencies = { + // eslint-disable-next-line new-cap + createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization + }; + + visualizations.types.VisTypesRegistryProvider.register(() => + createTableVisTypeDefinition(legacyDependencies) + ); + tableAggResponse = legacyResponseHandlerProvider().handler; indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); Vis = Private(VisProvider); diff --git a/src/legacy/core_plugins/table_vis/public/agg_table/agg_table.js b/src/legacy/core_plugins/table_vis/public/agg_table/agg_table.js index 858dedb3171ca..fa5ee90df3b32 100644 --- a/src/legacy/core_plugins/table_vis/public/agg_table/agg_table.js +++ b/src/legacy/core_plugins/table_vis/public/agg_table/agg_table.js @@ -16,226 +16,218 @@ * specific language governing permissions and limitations * under the License. */ - -import 'angular'; -import 'angular-recursion'; -import '../paginated_table'; import _ from 'lodash'; -import { uiModules } from 'ui/modules'; import aggTableTemplate from './agg_table.html'; import { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; import { i18n } from '@kbn/i18n'; -uiModules - .get('kibana', ['RecursionHelper']) - .directive('kbnAggTable', function (config, RecursionHelper) { - - return { - restrict: 'E', - template: aggTableTemplate, - scope: { - table: '=', - dimensions: '=', - perPage: '=?', - sort: '=?', - exportTitle: '=?', - showTotal: '=', - totalFunc: '=', - percentageCol: '=', - filter: '=', - }, - controllerAs: 'aggTable', - compile: function ($el) { +export function KbnAggTable(config, RecursionHelper) { + return { + restrict: 'E', + template: aggTableTemplate, + scope: { + table: '=', + dimensions: '=', + perPage: '=?', + sort: '=?', + exportTitle: '=?', + showTotal: '=', + totalFunc: '=', + percentageCol: '=', + filter: '=', + }, + controllerAs: 'aggTable', + compile: function ($el) { // Use the compile function from the RecursionHelper, // And return the linking function(s) which it returns - return RecursionHelper.compile($el); - }, - controller: function ($scope) { - const self = this; - - self._saveAs = require('@elastic/filesaver').saveAs; - self.csv = { - separator: config.get('csv:separator'), - quoteValues: config.get('csv:quoteValues') - }; - - self.exportAsCsv = function (formatted) { - const csv = new Blob([self.toCsv(formatted)], { type: 'text/plain;charset=utf-8' }); - self._saveAs(csv, self.csv.filename); - }; - - self.toCsv = function (formatted) { - const rows = $scope.table.rows; - const columns = formatted ? $scope.formattedColumns : $scope.table.columns; - const nonAlphaNumRE = /[^a-zA-Z0-9]/; - const allDoubleQuoteRE = /"/g; - - function escape(val) { - if (!formatted && _.isObject(val)) val = val.valueOf(); - val = String(val); - if (self.csv.quoteValues && nonAlphaNumRE.test(val)) { - val = '"' + val.replace(allDoubleQuoteRE, '""') + '"'; - } - return val; + return RecursionHelper.compile($el); + }, + controller: function ($scope) { + const self = this; + + self._saveAs = require('@elastic/filesaver').saveAs; + self.csv = { + separator: config.get('csv:separator'), + quoteValues: config.get('csv:quoteValues'), + }; + + self.exportAsCsv = function (formatted) { + const csv = new Blob([self.toCsv(formatted)], { type: 'text/plain;charset=utf-8' }); + self._saveAs(csv, self.csv.filename); + }; + + self.toCsv = function (formatted) { + const rows = $scope.table.rows; + const columns = formatted ? $scope.formattedColumns : $scope.table.columns; + const nonAlphaNumRE = /[^a-zA-Z0-9]/; + const allDoubleQuoteRE = /"/g; + + function escape(val) { + if (!formatted && _.isObject(val)) val = val.valueOf(); + val = String(val); + if (self.csv.quoteValues && nonAlphaNumRE.test(val)) { + val = '"' + val.replace(allDoubleQuoteRE, '""') + '"'; } + return val; + } - // escape each cell in each row - const csvRows = rows.map(function (row) { - return Object.entries(row).map(([k, v]) => { - return escape(formatted ? columns.find(c => c.id === k).formatter.convert(v) : v); - }); + // escape each cell in each row + const csvRows = rows.map(function (row) { + return Object.entries(row).map(([k, v]) => { + return escape(formatted ? columns.find(c => c.id === k).formatter.convert(v) : v); }); + }); + + // add the columns to the rows + csvRows.unshift(columns.map(function (col) { + return escape(formatted ? col.title : col.name); + })); + + return csvRows.map(function (row) { + return row.join(self.csv.separator) + '\r\n'; + }).join(''); + }; + + $scope.$watchMulti( + ['table', 'exportTitle', 'percentageCol', 'totalFunc', '=scope.dimensions'], + function () { + const { table, exportTitle, percentageCol } = $scope; + const showPercentage = percentageCol !== ''; + + if (!table) { + $scope.rows = null; + $scope.formattedColumns = null; + return; + } - // add the columns to the rows - csvRows.unshift(columns.map(function (col) { - return escape(formatted ? col.title : col.name); - })); - - return csvRows.map(function (row) { - return row.join(self.csv.separator) + '\r\n'; - }).join(''); - }; - - $scope.$watchMulti( - ['table', 'exportTitle', 'percentageCol', 'totalFunc', '=scope.dimensions'], - function () { - const { table, exportTitle, percentageCol } = $scope; - const showPercentage = percentageCol !== ''; - - if (!table) { - $scope.rows = null; - $scope.formattedColumns = null; - return; - } - - self.csv.filename = (exportTitle || table.title || 'table') + '.csv'; - $scope.rows = table.rows; - $scope.formattedColumns = []; - - if (typeof $scope.dimensions === 'undefined') return; - - const { buckets, metrics } = $scope.dimensions; - - $scope.formattedColumns = table.columns - .map(function (col, i) { - const isBucket = buckets.find(bucket => bucket.accessor === i); - const dimension = isBucket || metrics.find(metric => metric.accessor === i); - - if (!dimension) return; - - const formatter = getFormat(dimension.format); - - const formattedColumn = { - id: col.id, - title: col.name, - formatter: formatter, - filterable: !!isBucket, + self.csv.filename = (exportTitle || table.title || 'table') + '.csv'; + $scope.rows = table.rows; + $scope.formattedColumns = []; + + if (typeof $scope.dimensions === 'undefined') return; + + const { buckets, metrics } = $scope.dimensions; + + $scope.formattedColumns = table.columns + .map(function (col, i) { + const isBucket = buckets.find(bucket => bucket.accessor === i); + const dimension = isBucket || metrics.find(metric => metric.accessor === i); + + if (!dimension) return; + + const formatter = getFormat(dimension.format); + + const formattedColumn = { + id: col.id, + title: col.name, + formatter: formatter, + filterable: !!isBucket, + }; + + const last = i === table.columns.length - 1; + + if (last || !isBucket) { + formattedColumn.class = 'visualize-table-right'; + } + + const isDate = + _.get(dimension, 'format.id') === 'date' || + _.get(dimension, 'format.params.id') === 'date'; + const isNumeric = + _.get(dimension, 'format.id') === 'number' || + _.get(dimension, 'format.params.id') === 'number'; + + let { totalFunc } = $scope; + if (typeof totalFunc === 'undefined' && showPercentage) { + totalFunc = 'sum'; + } + + if (isNumeric || isDate || totalFunc === 'count') { + const sum = tableRows => { + return _.reduce( + tableRows, + function (prev, curr) { + // some metrics return undefined for some of the values + // derivative is an example of this as it returns undefined in the first row + if (curr[col.id] === undefined) return prev; + return prev + curr[col.id]; + }, + 0, + ); }; - const last = i === table.columns.length - 1; - - if (last || !isBucket) { - formattedColumn.class = 'visualize-table-right'; - } - - const isDate = - _.get(dimension, 'format.id') === 'date' || - _.get(dimension, 'format.params.id') === 'date'; - const isNumeric = - _.get(dimension, 'format.id') === 'number' || - _.get(dimension, 'format.params.id') === 'number'; + formattedColumn.sumTotal = sum(table.rows); - let { totalFunc } = $scope; - if (typeof totalFunc === 'undefined' && showPercentage) { - totalFunc = 'sum'; - } - - if (isNumeric || isDate || totalFunc === 'count') { - const sum = tableRows => { - return _.reduce( - tableRows, - function (prev, curr) { - // some metrics return undefined for some of the values - // derivative is an example of this as it returns undefined in the first row - if (curr[col.id] === undefined) return prev; - return prev + curr[col.id]; - }, - 0 - ); - }; - - formattedColumn.sumTotal = sum(table.rows); - - switch (totalFunc) { - case 'sum': { - if (!isDate) { - const total = formattedColumn.sumTotal; - formattedColumn.formattedTotal = formatter.convert(total); - formattedColumn.total = formattedColumn.sumTotal; - } - break; - } - case 'avg': { - if (!isDate) { - const total = sum(table.rows) / table.rows.length; - formattedColumn.formattedTotal = formatter.convert(total); - formattedColumn.total = total; - } - break; - } - case 'min': { - const total = _.chain(table.rows) - .map(col.id) - .min() - .value(); + switch (totalFunc) { + case 'sum': { + if (!isDate) { + const total = formattedColumn.sumTotal; formattedColumn.formattedTotal = formatter.convert(total); - formattedColumn.total = total; - break; + formattedColumn.total = formattedColumn.sumTotal; } - case 'max': { - const total = _.chain(table.rows) - .map(col.id) - .max() - .value(); + break; + } + case 'avg': { + if (!isDate) { + const total = sum(table.rows) / table.rows.length; formattedColumn.formattedTotal = formatter.convert(total); formattedColumn.total = total; - break; - } - case 'count': { - const total = table.rows.length; - formattedColumn.formattedTotal = total; - formattedColumn.total = total; - break; } - default: - break; + break; } + case 'min': { + const total = _.chain(table.rows) + .map(col.id) + .min() + .value(); + formattedColumn.formattedTotal = formatter.convert(total); + formattedColumn.total = total; + break; + } + case 'max': { + const total = _.chain(table.rows) + .map(col.id) + .max() + .value(); + formattedColumn.formattedTotal = formatter.convert(total); + formattedColumn.total = total; + break; + } + case 'count': { + const total = table.rows.length; + formattedColumn.formattedTotal = total; + formattedColumn.total = total; + break; + } + default: + break; } - - return formattedColumn; - }) - .filter(column => column); - - if (showPercentage) { - const insertAtIndex = _.findIndex($scope.formattedColumns, { title: percentageCol }); - - // column to show percentage for was removed - if (insertAtIndex < 0) return; - - const { cols, rows } = addPercentageCol( - $scope.formattedColumns, - percentageCol, - table.rows, - insertAtIndex - ); - $scope.rows = rows; - $scope.formattedColumns = cols; - } + } + + return formattedColumn; + }) + .filter(column => column); + + if (showPercentage) { + const insertAtIndex = _.findIndex($scope.formattedColumns, { title: percentageCol }); + + // column to show percentage for was removed + if (insertAtIndex < 0) return; + + const { cols, rows } = addPercentageCol( + $scope.formattedColumns, + percentageCol, + table.rows, + insertAtIndex, + ); + $scope.rows = rows; + $scope.formattedColumns = cols; } - ); - }, - }; - }); + }, + ); + }, + }; +} /** * @param {[]Object} columns - the formatted columns that will be displayed diff --git a/src/legacy/core_plugins/table_vis/public/agg_table/agg_table_group.js b/src/legacy/core_plugins/table_vis/public/agg_table/agg_table_group.js index 940325f145a44..1b2e5379e2c4e 100644 --- a/src/legacy/core_plugins/table_vis/public/agg_table/agg_table_group.js +++ b/src/legacy/core_plugins/table_vis/public/agg_table/agg_table_group.js @@ -17,46 +17,40 @@ * under the License. */ -import 'angular'; -import 'angular-recursion'; -import './'; -import { uiModules } from 'ui/modules'; import aggTableGroupTemplate from './agg_table_group.html'; -uiModules - .get('kibana', ['RecursionHelper']) - .directive('kbnAggTableGroup', function (RecursionHelper) { - return { - restrict: 'E', - template: aggTableGroupTemplate, - scope: { - group: '=', - dimensions: '=', - perPage: '=?', - sort: '=?', - exportTitle: '=?', - showTotal: '=', - totalFunc: '=', - percentageCol: '=', - filter: '=', - }, - compile: function ($el) { +export function KbnAggTableGroup(RecursionHelper) { + return { + restrict: 'E', + template: aggTableGroupTemplate, + scope: { + group: '=', + dimensions: '=', + perPage: '=?', + sort: '=?', + exportTitle: '=?', + showTotal: '=', + totalFunc: '=', + percentageCol: '=', + filter: '=', + }, + compile: function ($el) { // Use the compile function from the RecursionHelper, // And return the linking function(s) which it returns - return RecursionHelper.compile($el, { - post: function ($scope) { - $scope.$watch('group', function (group) { + return RecursionHelper.compile($el, { + post: function ($scope) { + $scope.$watch('group', function (group) { // clear the previous "state" - $scope.rows = $scope.columns = false; + $scope.rows = $scope.columns = false; - if (!group || !group.tables.length) return; + if (!group || !group.tables.length) return; - const childLayout = (group.direction === 'row') ? 'rows' : 'columns'; + const childLayout = (group.direction === 'row') ? 'rows' : 'columns'; - $scope[childLayout] = group.tables; - }); - } - }); - } - }; - }); + $scope[childLayout] = group.tables; + }); + }, + }); + }, + }; +} diff --git a/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/index.js b/src/legacy/core_plugins/table_vis/public/index.ts similarity index 77% rename from src/legacy/core_plugins/table_vis/public/agg_table/__tests__/index.js rename to src/legacy/core_plugins/table_vis/public/index.ts index efa53cd80690d..efbaf69659ea2 100644 --- a/src/legacy/core_plugins/table_vis/public/agg_table/__tests__/index.js +++ b/src/legacy/core_plugins/table_vis/public/index.ts @@ -17,7 +17,9 @@ * under the License. */ -import './_group'; -import './_table'; -describe('AggTable Component', function () { -}); +import { PluginInitializerContext } from '../../../../core/public'; +import { TableVisPlugin as Plugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new Plugin(initializerContext); +} diff --git a/src/legacy/core_plugins/table_vis/public/agg_table/index.js b/src/legacy/core_plugins/table_vis/public/legacy.ts similarity index 51% rename from src/legacy/core_plugins/table_vis/public/agg_table/index.js rename to src/legacy/core_plugins/table_vis/public/legacy.ts index 26335047f517d..fded5690a362d 100644 --- a/src/legacy/core_plugins/table_vis/public/agg_table/index.js +++ b/src/legacy/core_plugins/table_vis/public/legacy.ts @@ -17,4 +17,24 @@ * under the License. */ -import './agg_table'; +import { PluginInitializerContext } from 'kibana/public'; +import { npSetup, npStart } from 'ui/new_platform'; +import { plugin } from '.'; + +import { TablePluginSetupDependencies } from './plugin'; +import { visualizations } from '../../visualizations/public'; +import { LegacyDependenciesPlugin } from './shim'; + +const plugins: Readonly = { + visualizations, + data: npSetup.plugins.data, + + // Temporary solution + // It will be removed when all dependent services are migrated to the new platform. + __LEGACY: new LegacyDependenciesPlugin(), +}; + +const pluginInstance = plugin({} as PluginInitializerContext); + +export const setup = pluginInstance.setup(npSetup.core, plugins); +export const start = pluginInstance.start(npStart.core); diff --git a/src/legacy/core_plugins/table_vis/public/paginated_table/__tests__/index.js b/src/legacy/core_plugins/table_vis/public/paginated_table/__tests__/paginated_table.js similarity index 99% rename from src/legacy/core_plugins/table_vis/public/paginated_table/__tests__/index.js rename to src/legacy/core_plugins/table_vis/public/paginated_table/__tests__/paginated_table.js index dcaed42464036..e2571f9abb685 100644 --- a/src/legacy/core_plugins/table_vis/public/paginated_table/__tests__/index.js +++ b/src/legacy/core_plugins/table_vis/public/paginated_table/__tests__/paginated_table.js @@ -21,10 +21,10 @@ import _ from 'lodash'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import '..'; + import $ from 'jquery'; -describe('paginated table', function () { +describe('Table Vis - Paginated table', function () { let $el; let $rootScope; let $compile; diff --git a/src/legacy/core_plugins/table_vis/public/paginated_table/paginated_table.js b/src/legacy/core_plugins/table_vis/public/paginated_table/paginated_table.js index cd214e303d7d3..ce66bd912b7c8 100644 --- a/src/legacy/core_plugins/table_vis/public/paginated_table/paginated_table.js +++ b/src/legacy/core_plugins/table_vis/public/paginated_table/paginated_table.js @@ -18,111 +18,106 @@ */ import _ from 'lodash'; -import { uiModules } from 'ui/modules'; import paginatedTableTemplate from './paginated_table.html'; -import 'ui/directives/paginate'; +export function PaginatedTable($filter) { + const orderBy = $filter('orderBy'); -uiModules - .get('kibana') - .directive('paginatedTable', function ($filter) { - const orderBy = $filter('orderBy'); + return { + restrict: 'E', + template: paginatedTableTemplate, + transclude: true, + scope: { + table: '=', + rows: '=', + columns: '=', + linkToTop: '=', + perPage: '=?', + showBlankRows: '=?', + sortHandler: '=?', + sort: '=?', + showSelector: '=?', + showTotal: '=', + totalFunc: '=', + filter: '=', + percentageCol: '=', + }, + controllerAs: 'paginatedTable', + controller: function ($scope) { + const self = this; + self.sort = { + columnIndex: null, + direction: null, + }; - return { - restrict: 'E', - template: paginatedTableTemplate, - transclude: true, - scope: { - table: '=', - rows: '=', - columns: '=', - linkToTop: '=', - perPage: '=?', - showBlankRows: '=?', - sortHandler: '=?', - sort: '=?', - showSelector: '=?', - showTotal: '=', - totalFunc: '=', - filter: '=', - percentageCol: '=', - }, - controllerAs: 'paginatedTable', - controller: function ($scope) { - const self = this; - self.sort = { - columnIndex: null, - direction: null - }; + self.sortColumn = function (colIndex, sortDirection = 'asc') { + const col = $scope.columns[colIndex]; - self.sortColumn = function (colIndex, sortDirection = 'asc') { - const col = $scope.columns[colIndex]; + if (!col) return; + if (col.sortable === false) return; - if (!col) return; - if (col.sortable === false) return; - - if (self.sort.columnIndex === colIndex) { - const directions = { - null: 'asc', - 'asc': 'desc', - 'desc': null - }; - sortDirection = directions[self.sort.direction]; - } - - self.sort.columnIndex = colIndex; - self.sort.direction = sortDirection; - if ($scope.sort) { - _.assign($scope.sort, self.sort); - } - }; - - self.rowsToShow = function (numRowsPerPage, actualNumRowsOnThisPage) { - if ($scope.showBlankRows === false) { - return actualNumRowsOnThisPage; - } else { - return numRowsPerPage; - } - }; + if (self.sort.columnIndex === colIndex) { + const directions = { + null: 'asc', + asc: 'desc', + desc: null, + }; + sortDirection = directions[self.sort.direction]; + } - function valueGetter(row) { - const col = $scope.columns[self.sort.columnIndex]; - let value = row[col.id]; - if (typeof value === 'boolean') value = value ? 0 : 1; - return value; + self.sort.columnIndex = colIndex; + self.sort.direction = sortDirection; + if ($scope.sort) { + _.assign($scope.sort, self.sort); } + }; - // Set the sort state if it is set - if ($scope.sort && $scope.sort.columnIndex !== null) { - self.sortColumn($scope.sort.columnIndex, $scope.sort.direction); + self.rowsToShow = function (numRowsPerPage, actualNumRowsOnThisPage) { + if ($scope.showBlankRows === false) { + return actualNumRowsOnThisPage; + } else { + return numRowsPerPage; } - function resortRows() { - const newSort = $scope.sort; - if (newSort && !_.isEqual(newSort, self.sort)) { - self.sortColumn(newSort.columnIndex, newSort.direction); - } + }; + + function valueGetter(row) { + const col = $scope.columns[self.sort.columnIndex]; + let value = row[col.id]; + if (typeof value === 'boolean') value = value ? 0 : 1; + return value; + } - if (!$scope.rows || !$scope.columns) { - $scope.sortedRows = false; - return; - } + // Set the sort state if it is set + if ($scope.sort && $scope.sort.columnIndex !== null) { + self.sortColumn($scope.sort.columnIndex, $scope.sort.direction); + } - const sort = self.sort; - if (sort.direction == null) { - $scope.sortedRows = $scope.rows.slice(0); - } else { - $scope.sortedRows = orderBy($scope.rows, valueGetter, sort.direction === 'desc'); - } + function resortRows() { + const newSort = $scope.sort; + if (newSort && !_.isEqual(newSort, self.sort)) { + self.sortColumn(newSort.columnIndex, newSort.direction); } + if (!$scope.rows || !$scope.columns) { + $scope.sortedRows = false; + return; + } - // update the sortedRows result - $scope.$watchMulti([ - 'rows', - 'columns', - '[]sort', - '[]paginatedTable.sort' - ], resortRows); + const sort = self.sort; + if (sort.direction == null) { + $scope.sortedRows = $scope.rows.slice(0); + } else { + $scope.sortedRows = orderBy($scope.rows, valueGetter, sort.direction === 'desc'); + } } - }; - }); + + // update the sortedRows result + $scope.$watchMulti([ + 'rows', + 'columns', + '[]sort', + '[]paginatedTable.sort', + ], resortRows); + }, + }; +} diff --git a/src/legacy/core_plugins/table_vis/public/paginated_table/rows.js b/src/legacy/core_plugins/table_vis/public/paginated_table/rows.js index bef97c5bb83b9..0a7c43a264b8f 100644 --- a/src/legacy/core_plugins/table_vis/public/paginated_table/rows.js +++ b/src/legacy/core_plugins/table_vis/public/paginated_table/rows.js @@ -19,12 +19,9 @@ import $ from 'jquery'; import _ from 'lodash'; -import { uiModules } from 'ui/modules'; import tableCellFilterHtml from './table_cell_filter.html'; -const module = uiModules.get('kibana'); - -module.directive('kbnRows', function ($compile) { +export function KbnRows($compile) { return { restrict: 'A', link: function ($scope, $el, attr) { @@ -45,12 +42,14 @@ module.directive('kbnRows', function ($compile) { return; } - $scope.filter({ data: [{ - table: $scope.table, - row: $scope.rows.findIndex(r => r === row), - column: $scope.table.columns.findIndex(c => c.id === column.id), - value - }], negate }); + $scope.filter({ + data: [{ + table: $scope.table, + row: $scope.rows.findIndex(r => r === row), + column: $scope.table.columns.findIndex(c => c.id === column.id), + value, + }], negate, + }); }; return $compile($template)(scope); @@ -103,7 +102,7 @@ module.directive('kbnRows', function ($compile) { $scope.$watchMulti([ attr.kbnRows, - attr.kbnRowsMin + attr.kbnRowsMin, ], function (vals) { let rows = vals[0]; const min = vals[1]; @@ -118,7 +117,9 @@ module.directive('kbnRows', function ($compile) { // crate the empty row which will be pushed into the row list over and over const emptyRow = {}; // push as many empty rows into the row array as needed - _.times(min - rows.length, function () { rows.push(emptyRow); }); + _.times(min - rows.length, function () { + rows.push(emptyRow); + }); } rows.forEach(function (row) { @@ -129,6 +130,6 @@ module.directive('kbnRows', function ($compile) { }); }); }); - } + }, }; -}); +} diff --git a/src/legacy/core_plugins/table_vis/public/plugin.ts b/src/legacy/core_plugins/table_vis/public/plugin.ts new file mode 100644 index 0000000000000..1f1d6b7dc7453 --- /dev/null +++ b/src/legacy/core_plugins/table_vis/public/plugin.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 { VisualizationsSetup } from '../../visualizations/public'; +import { Plugin as DataPublicPlugin } from '../../../../plugins/data/public'; + +import { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + UiSettingsClientContract, +} from '../../../../core/public'; + +import { LegacyDependenciesPluginSetup, LegacyDependenciesPlugin } from './shim'; + +// @ts-ignore +import { createTableVisFn } from './table_vis_fn'; +// @ts-ignore +import { createTableVisTypeDefinition } from './table_vis_type'; + +/** @private */ +export interface TableVisualizationDependencies extends LegacyDependenciesPluginSetup { + uiSettings: UiSettingsClientContract; +} + +/** @internal */ +export interface TablePluginSetupDependencies { + data: ReturnType; + visualizations: VisualizationsSetup; + __LEGACY: LegacyDependenciesPlugin; +} + +/** @internal */ +export class TableVisPlugin implements Plugin, void> { + initializerContext: PluginInitializerContext; + + constructor(initializerContext: PluginInitializerContext) { + this.initializerContext = initializerContext; + } + + public async setup( + core: CoreSetup, + { data, visualizations, __LEGACY }: TablePluginSetupDependencies + ) { + const visualizationDependencies: Readonly = { + uiSettings: core.uiSettings, + ...(await __LEGACY.setup()), + }; + + data.expressions.registerFunction(() => createTableVisFn(visualizationDependencies)); + + visualizations.types.VisTypesRegistryProvider.register(() => + createTableVisTypeDefinition(visualizationDependencies) + ); + } + + public start(core: CoreStart) { + // nothing to do here yet + } +} diff --git a/src/legacy/core_plugins/table_vis/public/paginated_table/index.js b/src/legacy/core_plugins/table_vis/public/shim/index.ts similarity index 94% rename from src/legacy/core_plugins/table_vis/public/paginated_table/index.js rename to src/legacy/core_plugins/table_vis/public/shim/index.ts index 4ca09bf7e2205..cfc7b62ff4f86 100644 --- a/src/legacy/core_plugins/table_vis/public/paginated_table/index.js +++ b/src/legacy/core_plugins/table_vis/public/shim/index.ts @@ -17,5 +17,4 @@ * under the License. */ -import './rows'; -import './paginated_table'; +export * from './legacy_dependencies_plugin'; diff --git a/src/legacy/core_plugins/table_vis/public/shim/legacy_dependencies_plugin.ts b/src/legacy/core_plugins/table_vis/public/shim/legacy_dependencies_plugin.ts new file mode 100644 index 0000000000000..91e5c64646cf9 --- /dev/null +++ b/src/legacy/core_plugins/table_vis/public/shim/legacy_dependencies_plugin.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. + */ + +import chrome from 'ui/chrome'; + +// @ts-ignore +import { VisFactoryProvider } from 'ui/vis/vis_factory'; + +import { CoreStart, Plugin } from '../../../../../core/public'; +import { initTableVisLegacyModule } from './table_vis_legacy_module'; + +/** @internal */ +export interface LegacyDependenciesPluginSetup { + createAngularVisualization: Function; +} + +/** @internal */ +export class LegacyDependenciesPlugin + implements Plugin, void> { + public async setup() { + initTableVisLegacyModule(); + + const $injector = await chrome.dangerouslyGetActiveInjector(); + const Private = $injector.get('Private'); + + return { + createAngularVisualization: VisFactoryProvider(Private).createAngularVisualization, + } as LegacyDependenciesPluginSetup; + } + + public start(core: CoreStart) { + // nothing to do here yet + } +} diff --git a/src/legacy/core_plugins/table_vis/public/shim/table_vis_legacy_module.ts b/src/legacy/core_plugins/table_vis/public/shim/table_vis_legacy_module.ts new file mode 100644 index 0000000000000..7750804fb5879 --- /dev/null +++ b/src/legacy/core_plugins/table_vis/public/shim/table_vis_legacy_module.ts @@ -0,0 +1,51 @@ +/* + * 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 { once } from 'lodash'; + +// @ts-ignore +import { uiModules } from 'ui/modules'; + +import 'angular-recursion'; +import 'ui/directives/paginate'; + +// @ts-ignore +import { TableVisController } from '../table_vis_controller.js'; +// @ts-ignore +import { TableVisParams } from '../table_vis_params'; +// @ts-ignore +import { KbnAggTable } from '../agg_table/agg_table'; +// @ts-ignore +import { KbnAggTableGroup } from '../agg_table/agg_table_group'; +// @ts-ignore +import { KbnRows } from '../paginated_table/rows'; +// @ts-ignore +import { PaginatedTable } from '../paginated_table/paginated_table'; + +/** @internal */ +export const initTableVisLegacyModule = once((): void => { + uiModules + .get('kibana/table_vis', ['kibana', 'RecursionHelper']) + .controller('KbnTableVisController', TableVisController) + .directive('tableVisParams', TableVisParams) + .directive('kbnAggTable', KbnAggTable) + .directive('kbnAggTableGroup', KbnAggTableGroup) + .directive('kbnRows', KbnRows) + .directive('paginatedTable', PaginatedTable); +}); diff --git a/src/legacy/core_plugins/table_vis/public/table_vis_controller.js b/src/legacy/core_plugins/table_vis/public/table_vis_controller.js index 55826b69eeb4d..210e074cac213 100644 --- a/src/legacy/core_plugins/table_vis/public/table_vis_controller.js +++ b/src/legacy/core_plugins/table_vis/public/table_vis_controller.js @@ -16,17 +16,9 @@ * specific language governing permissions and limitations * under the License. */ - -import { uiModules } from 'ui/modules'; import { assign } from 'lodash'; -// get the kibana/table_vis module, and make sure that it requires the "kibana" module if it -// didn't already -const module = uiModules.get('kibana/table_vis', ['kibana']); - -// add a controller to tha module, which will transform the esResponse into a -// tabular format that we can pass to the table directive -module.controller('KbnTableVisController', function ($scope) { +export function TableVisController($scope) { const uiStateSort = ($scope.uiState) ? $scope.uiState.get('vis.params.sort') : {}; assign($scope.visParams.sort, uiStateSort); @@ -61,5 +53,6 @@ module.controller('KbnTableVisController', function ($scope) { } $scope.renderComplete(); }); -}); +} + diff --git a/src/legacy/core_plugins/table_vis/public/table_vis_fn.js b/src/legacy/core_plugins/table_vis/public/table_vis_fn.js index 4176902c3db63..0a5bbc9d6e37f 100644 --- a/src/legacy/core_plugins/table_vis/public/table_vis_fn.js +++ b/src/legacy/core_plugins/table_vis/public/table_vis_fn.js @@ -17,11 +17,10 @@ * under the License. */ -import { functionsRegistry } from 'plugins/interpreter/registries'; -import { legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy'; import { i18n } from '@kbn/i18n'; +import { createTableVisResponseHandler } from './table_vis_request_handler'; -export const kibanaTable = () => ({ +export const createTableVisFn = (dependencies) => ({ name: 'kibana_table', type: 'render', context: { @@ -40,8 +39,7 @@ export const kibanaTable = () => ({ }, async fn(context, args) { const visConfig = JSON.parse(args.visConfig); - - const responseHandler = legacyResponseHandlerProvider().handler; + const responseHandler = createTableVisResponseHandler(dependencies); const convertedData = await responseHandler(context, visConfig.dimensions); return { @@ -58,5 +56,3 @@ export const kibanaTable = () => ({ }; }, }); - -functionsRegistry.register(kibanaTable); diff --git a/src/legacy/core_plugins/table_vis/public/table_vis_fn.test.js b/src/legacy/core_plugins/table_vis/public/table_vis_fn.test.js index f7aaa60e67048..a35eb85b07f1a 100644 --- a/src/legacy/core_plugins/table_vis/public/table_vis_fn.test.js +++ b/src/legacy/core_plugins/table_vis/public/table_vis_fn.test.js @@ -18,7 +18,7 @@ */ import { functionWrapper } from '../../interpreter/test_helpers'; -import { kibanaTable } from './table_vis_fn'; +import { createTableVisFn } from './table_vis_fn'; jest.mock('ui/new_platform'); @@ -30,7 +30,7 @@ jest.mock('ui/vis/response_handlers/legacy', () => ({ })); describe('interpreter/functions#table', () => { - const fn = functionWrapper(kibanaTable); + const fn = functionWrapper(createTableVisFn); const context = { type: 'kibana_datatable', rows: [{ 'col-0-1': 0 }], diff --git a/src/legacy/core_plugins/table_vis/public/table_vis_params.js b/src/legacy/core_plugins/table_vis/public/table_vis_params.js index a1519066841a4..993e6e2321cbf 100644 --- a/src/legacy/core_plugins/table_vis/public/table_vis_params.js +++ b/src/legacy/core_plugins/table_vis/public/table_vis_params.js @@ -17,13 +17,12 @@ * under the License. */ -import { uiModules } from 'ui/modules'; -import { tabifyGetColumns } from 'ui/agg_response/tabify/_get_columns.js'; -import tableVisParamsTemplate from './table_vis_params.html'; import _ from 'lodash'; +import { tabifyGetColumns } from 'ui/agg_response/tabify/_get_columns'; +import tableVisParamsTemplate from './table_vis_params.html'; import { i18n } from '@kbn/i18n'; -uiModules.get('kibana/table_vis').directive('tableVisParams', function () { +export function TableVisParams() { return { restrict: 'E', template: tableVisParamsTemplate, @@ -65,7 +64,7 @@ uiModules.get('kibana/table_vis').directive('tableVisParams', function () { ); }, }; -}); +} /** * Determines if a aggConfig is numeric diff --git a/src/legacy/core_plugins/table_vis/public/__tests__/index.js b/src/legacy/core_plugins/table_vis/public/table_vis_request_handler.js similarity index 82% rename from src/legacy/core_plugins/table_vis/public/__tests__/index.js rename to src/legacy/core_plugins/table_vis/public/table_vis_request_handler.js index 1f4f3ec30ee4a..bfec2711413a1 100644 --- a/src/legacy/core_plugins/table_vis/public/__tests__/index.js +++ b/src/legacy/core_plugins/table_vis/public/table_vis_request_handler.js @@ -17,7 +17,6 @@ * under the License. */ -import './_table_vis_controller'; +import { legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy'; -describe('Table Vis', function () { -}); +export const createTableVisResponseHandler = () => legacyResponseHandlerProvider().handler; diff --git a/src/legacy/core_plugins/table_vis/public/table_vis.js b/src/legacy/core_plugins/table_vis/public/table_vis_type.js similarity index 70% rename from src/legacy/core_plugins/table_vis/public/table_vis.js rename to src/legacy/core_plugins/table_vis/public/table_vis_type.js index 501c3c323afbb..03c3835645bad 100644 --- a/src/legacy/core_plugins/table_vis/public/table_vis.js +++ b/src/legacy/core_plugins/table_vis/public/table_vis_type.js @@ -18,39 +18,15 @@ */ import { i18n } from '@kbn/i18n'; -import './table_vis_controller'; -import './table_vis_params'; -import './agg_table'; -import './agg_table/agg_table_group'; -import { VisFactoryProvider } from 'ui/vis/vis_factory'; +import { createTableVisResponseHandler } from './table_vis_request_handler'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import tableVisTemplate from './table_vis.html'; -import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; -import { legacyResponseHandlerProvider } from 'ui/vis/response_handlers/legacy'; - -// we need to load the css ourselves - -// we also need to load the controller and used by the template - -// our params are a bit complex so we will manage them with a directive -// require the directives that we use as well - -// register the provider with the visTypes registry -VisTypesRegistryProvider.register(TableVisTypeProvider); - -const legacyTableResponseHandler = legacyResponseHandlerProvider().handler; - -// define the TableVisType -function TableVisTypeProvider(Private) { - const VisFactory = Private(VisFactoryProvider); +import tableVisTemplate from './table_vis.html'; - // define the TableVisController which is used in the template - // by angular's ng-controller directive +export const createTableVisTypeDefinition = (dependencies) => { + const responseHandler = createTableVisResponseHandler(dependencies); - // return the visType object, which kibana will use to display and configure new - // Vis object of this type. - return VisFactory.createAngularVisualization({ + return dependencies.createAngularVisualization({ type: 'table', name: 'table', title: i18n.translate('tableVis.tableVisTitle', { @@ -115,11 +91,10 @@ function TableVisTypeProvider(Private) { } ]) }, - responseHandler: legacyTableResponseHandler, + responseHandler, hierarchicalData: function (vis) { return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); } }); -} +}; -export default TableVisTypeProvider; diff --git a/src/legacy/ui/public/vis/__tests__/_agg_config.js b/src/legacy/ui/public/vis/__tests__/_agg_config.js index 1929b4312e14f..2dc3372f7928e 100644 --- a/src/legacy/ui/public/vis/__tests__/_agg_config.js +++ b/src/legacy/ui/public/vis/__tests__/_agg_config.js @@ -448,7 +448,6 @@ describe('AggConfig', function () { describe('#fieldFormatter - no custom getFormat handler', function () { const visStateAggWithoutCustomGetFormat = { - type: 'table', aggs: [ { type: 'histogram',