From 27a53d166fe834cc9106807045c39e9362423e30 Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Thu, 3 Oct 2019 10:15:31 +0200 Subject: [PATCH 01/35] Add TypeScript rules to STYLEGUIDE [skip ci] (#47125) * Add TypeScript rules to STYLEGUIDE * Update STYLEGUIDE.md Co-Authored-By: Court Ewing --- STYLEGUIDE.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 152a0e2c48871..5fd3ef5e8ff4b 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -141,6 +141,39 @@ function addBar(foos, foo) { } ``` +### Avoid `any` whenever possible + +Since TypeScript 3.0 and the introduction of the +[`unknown` type](https://mariusschulz.com/blog/the-unknown-type-in-typescript) there are rarely any +reasons to use `any` as a type. Nearly all places of former `any` usage can be replace by either a +generic or `unknown` (in cases the type is really not known). + +You should always prefer using those mechanisms over using `any`, since they are stricter typed and +less likely to introduce bugs in the future due to insufficient types. + +If you’re not having `any` in your plugin or are starting a new plugin, you should enable the +[`@typescript-eslint/no-explicit-any`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md) +linting rule for your plugin via the [`.eslintrc.js`](https://github.com/elastic/kibana/blob/master/.eslintrc.js) config. + +### Avoid non-null assertions + +You should try avoiding non-null assertions (`!.`) wherever possible. By using them you tell +TypeScript, that something is not null even though by it’s type it could be. Usage of non-null +assertions is most often a side-effect of you actually checked that the variable is not `null` +but TypeScript doesn’t correctly carry on that information till the usage of the variable. + +In most cases it’s possible to replace the non-null assertion by structuring your code/checks slightly different +or using [user defined type guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) +to properly tell TypeScript what type a variable has. + +Using non-null assertion increases the risk for future bugs. In case the condition under which we assumed that the +variable can’t be null has changed (potentially even due to changes in compeltely different files), the non-null +assertion would now wrongly disable proper type checking for us. + +If you’re not using non-null assertions in your plugin or are starting a new plugin, consider enabling the +[`@typescript-eslint/no-non-null-assertion`](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md) +linting rule for you plugin in the [`.eslintrc.js`](https://github.com/elastic/kibana/blob/master/.eslintrc.js) config. + ### Return/throw early from functions To avoid deep nesting of if-statements, always return a function's value as early From cae19e80aeafbbba5b99b593a59f8037172f2f5f Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Thu, 3 Oct 2019 10:44:21 +0200 Subject: [PATCH 02/35] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20convert=20Inte?= =?UTF-8?q?rpreter=20.js=20->=20.ts=20(#44545)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 convert Interpreter .js -> .ts * fix: 🐛 fix TypeScript type errors * test: 💍 remove old snapshot --- ...ibana.test.js.snap => kibana.test.ts.snap} | 0 .../functions/__tests__/{font.js => font.ts} | 10 ++--- .../public/functions/{clog.js => clog.ts} | 4 +- .../public/functions/{index.js => index.ts} | 9 ++++- .../{kibana.test.js => kibana.test.ts} | 6 +-- .../public/functions/{kibana.js => kibana.ts} | 6 +-- .../{kibana_context.js => kibana_context.ts} | 19 ++++------ .../{vis_dimension.js => vis_dimension.ts} | 32 ++++++++-------- .../{visualization.js => visualization.ts} | 27 +++++++------- ...nterpreter.test.js => interpreter.test.ts} | 15 ++++---- .../public/{interpreter.js => interpreter.ts} | 10 +++-- ...{render_function.js => render_function.ts} | 4 +- ...gistry.js => render_functions_registry.ts} | 6 +-- .../{visualization.js => visualization.ts} | 13 ++++--- ...{create_handlers.js => create_handlers.ts} | 4 +- ...{create_handlers.js => create_handlers.ts} | 4 +- .../server/routes/{index.js => index.ts} | 2 +- ...erver_functions.js => server_functions.ts} | 37 ++++++++++--------- .../{test_helpers.js => test_helpers.ts} | 5 ++- .../public/markdown_fn.test.ts | 2 +- .../public/metric_vis_fn.test.ts | 2 +- .../public/table_vis_fn.test.ts | 4 +- .../public/tag_cloud_fn.test.ts | 2 +- 23 files changed, 118 insertions(+), 105 deletions(-) rename src/legacy/core_plugins/interpreter/public/functions/__snapshots__/{kibana.test.js.snap => kibana.test.ts.snap} (100%) rename src/legacy/core_plugins/interpreter/public/functions/__tests__/{font.js => font.ts} (96%) rename src/legacy/core_plugins/interpreter/public/functions/{clog.js => clog.ts} (91%) rename src/legacy/core_plugins/interpreter/public/functions/{index.js => index.ts} (92%) rename src/legacy/core_plugins/interpreter/public/functions/{kibana.test.js => kibana.test.ts} (97%) rename src/legacy/core_plugins/interpreter/public/functions/{kibana.js => kibana.ts} (93%) rename src/legacy/core_plugins/interpreter/public/functions/{kibana_context.js => kibana_context.ts} (87%) rename src/legacy/core_plugins/interpreter/public/functions/{vis_dimension.js => vis_dimension.ts} (74%) rename src/legacy/core_plugins/interpreter/public/functions/{visualization.js => visualization.ts} (91%) rename src/legacy/core_plugins/interpreter/public/{interpreter.test.js => interpreter.test.ts} (96%) rename src/legacy/core_plugins/interpreter/public/{interpreter.js => interpreter.ts} (87%) rename src/legacy/core_plugins/interpreter/public/lib/{render_function.js => render_function.ts} (92%) rename src/legacy/core_plugins/interpreter/public/lib/{render_functions_registry.js => render_functions_registry.ts} (88%) rename src/legacy/core_plugins/interpreter/public/renderers/{visualization.js => visualization.ts} (84%) rename src/legacy/core_plugins/interpreter/server/lib/__tests__/{create_handlers.js => create_handlers.ts} (95%) rename src/legacy/core_plugins/interpreter/server/lib/{create_handlers.js => create_handlers.ts} (88%) rename src/legacy/core_plugins/interpreter/server/routes/{index.js => index.ts} (95%) rename src/legacy/core_plugins/interpreter/server/routes/{server_functions.js => server_functions.ts} (84%) rename src/legacy/core_plugins/interpreter/{test_helpers.js => test_helpers.ts} (86%) diff --git a/src/legacy/core_plugins/interpreter/public/functions/__snapshots__/kibana.test.js.snap b/src/legacy/core_plugins/interpreter/public/functions/__snapshots__/kibana.test.ts.snap similarity index 100% rename from src/legacy/core_plugins/interpreter/public/functions/__snapshots__/kibana.test.js.snap rename to src/legacy/core_plugins/interpreter/public/functions/__snapshots__/kibana.test.ts.snap diff --git a/src/legacy/core_plugins/interpreter/public/functions/__tests__/font.js b/src/legacy/core_plugins/interpreter/public/functions/__tests__/font.ts similarity index 96% rename from src/legacy/core_plugins/interpreter/public/functions/__tests__/font.js rename to src/legacy/core_plugins/interpreter/public/functions/__tests__/font.ts index 4a7ebc1522f2a..8bf8052fee3b7 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/__tests__/font.js +++ b/src/legacy/core_plugins/interpreter/public/functions/__tests__/font.ts @@ -23,13 +23,13 @@ import { font } from '../font'; import { functionWrapper } from '../../../test_helpers'; describe('font', () => { - const fn = functionWrapper(font); + const fn: any = functionWrapper(font); describe('default output', () => { const result = fn(null); it('returns a style', () => { - expect(result) + (expect as any)(result) .to.have.property('type', 'style') .and.to.have.property('spec') .and.to.have.property('css'); @@ -40,8 +40,8 @@ describe('font', () => { describe('size', () => { it('sets font size', () => { const result = fn(null, { size: 20 }); - expect(result.spec).to.have.property('fontSize', '20px'); - expect(result.css).to.contain('font-size:20px'); + (expect as any)(result.spec).to.have.property('fontSize', '20px'); + (expect as any)(result.css).to.contain('font-size:20px'); }); it('defaults to 14px', () => { @@ -110,7 +110,7 @@ describe('font', () => { expect(result.css).to.contain('font-weight:400'); }); - it('defaults to \'normal\'', () => { + it("defaults to 'normal'", () => { const result = fn(null); expect(result.spec).to.have.property('fontWeight', 'normal'); expect(result.css).to.contain('font-weight:normal'); diff --git a/src/legacy/core_plugins/interpreter/public/functions/clog.js b/src/legacy/core_plugins/interpreter/public/functions/clog.ts similarity index 91% rename from src/legacy/core_plugins/interpreter/public/functions/clog.js rename to src/legacy/core_plugins/interpreter/public/functions/clog.ts index 634d166f5f0bb..4867726a42d72 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/clog.js +++ b/src/legacy/core_plugins/interpreter/public/functions/clog.ts @@ -20,8 +20,8 @@ export const clog = () => ({ name: 'clog', help: 'Outputs the context to the console', - fn: context => { - console.log(context); //eslint-disable-line no-console + fn: (context: any) => { + console.log(context); // eslint-disable-line no-console return context; }, }); diff --git a/src/legacy/core_plugins/interpreter/public/functions/index.js b/src/legacy/core_plugins/interpreter/public/functions/index.ts similarity index 92% rename from src/legacy/core_plugins/interpreter/public/functions/index.js rename to src/legacy/core_plugins/interpreter/public/functions/index.ts index 38c3920f91bd2..d86f033acb3d1 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/index.js +++ b/src/legacy/core_plugins/interpreter/public/functions/index.ts @@ -27,5 +27,12 @@ import { visualization } from './visualization'; import { visDimension } from './vis_dimension'; export const functions = [ - clog, esaggs, font, kibana, kibanaContext, range, visualization, visDimension, + clog, + esaggs, + font, + kibana, + kibanaContext, + range, + visualization, + visDimension, ]; diff --git a/src/legacy/core_plugins/interpreter/public/functions/kibana.test.js b/src/legacy/core_plugins/interpreter/public/functions/kibana.test.ts similarity index 97% rename from src/legacy/core_plugins/interpreter/public/functions/kibana.test.js rename to src/legacy/core_plugins/interpreter/public/functions/kibana.test.ts index 4757b9b12b50d..9f80449ac36be 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/kibana.test.js +++ b/src/legacy/core_plugins/interpreter/public/functions/kibana.test.ts @@ -22,9 +22,9 @@ import { kibana } from './kibana'; describe('interpreter/functions#kibana', () => { const fn = functionWrapper(kibana); - let context; - let initialContext; - let handlers; + let context: any; + let initialContext: any; + let handlers: any; beforeEach(() => { context = { timeRange: { from: '0', to: '1' } }; diff --git a/src/legacy/core_plugins/interpreter/public/functions/kibana.js b/src/legacy/core_plugins/interpreter/public/functions/kibana.ts similarity index 93% rename from src/legacy/core_plugins/interpreter/public/functions/kibana.js rename to src/legacy/core_plugins/interpreter/public/functions/kibana.ts index e0817d8e04b02..37ff337f58b8d 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/kibana.js +++ b/src/legacy/core_plugins/interpreter/public/functions/kibana.ts @@ -24,10 +24,10 @@ export const kibana = () => ({ type: 'kibana_context', context: {}, help: i18n.translate('interpreter.functions.kibana.help', { - defaultMessage: 'Gets kibana global context' + defaultMessage: 'Gets kibana global context', }), args: {}, - fn(context, args, handlers) { + fn(context: any, args: any, handlers: any) { const initialContext = handlers.getInitialContext ? handlers.getInitialContext() : {}; if (context.query) { @@ -45,7 +45,7 @@ export const kibana = () => ({ type: 'kibana_context', query: initialContext.query, filters: initialContext.filters, - timeRange: timeRange, + timeRange, }; }, }); diff --git a/src/legacy/core_plugins/interpreter/public/functions/kibana_context.js b/src/legacy/core_plugins/interpreter/public/functions/kibana_context.ts similarity index 87% rename from src/legacy/core_plugins/interpreter/public/functions/kibana_context.js rename to src/legacy/core_plugins/interpreter/public/functions/kibana_context.ts index 7b7294a87831d..2f2241a367094 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/kibana_context.js +++ b/src/legacy/core_plugins/interpreter/public/functions/kibana_context.ts @@ -24,13 +24,10 @@ export const kibanaContext = () => ({ name: 'kibana_context', type: 'kibana_context', context: { - types: [ - 'kibana_context', - 'null', - ], + types: ['kibana_context', 'null'], }, help: i18n.translate('interpreter.functions.kibana_context.help', { - defaultMessage: 'Updates kibana global context' + defaultMessage: 'Updates kibana global context', }), args: { q: { @@ -49,11 +46,11 @@ export const kibanaContext = () => ({ savedSearchId: { types: ['string', 'null'], default: null, - } + }, }, - async fn(context, args) { + async fn(context: any, args: any) { const $injector = await chrome.dangerouslyGetActiveInjector(); - const savedSearches = $injector.get('savedSearches'); + const savedSearches = $injector.get('savedSearches') as any; const queryArg = args.q ? JSON.parse(args.q) : []; let queries = Array.isArray(queryArg) ? queryArg : [queryArg]; let filters = args.filters ? JSON.parse(args.filters) : []; @@ -71,7 +68,7 @@ export const kibanaContext = () => ({ } if (context.filters) { - filters = filters.concat(context.filters).filter(f => !f.meta.disabled); + filters = filters.concat(context.filters).filter((f: any) => !f.meta.disabled); } const timeRange = args.timeRange ? JSON.parse(args.timeRange) : context.timeRange; @@ -79,8 +76,8 @@ export const kibanaContext = () => ({ return { type: 'kibana_context', query: queries, - filters: filters, - timeRange: timeRange, + filters, + timeRange, }; }, }); diff --git a/src/legacy/core_plugins/interpreter/public/functions/vis_dimension.js b/src/legacy/core_plugins/interpreter/public/functions/vis_dimension.ts similarity index 74% rename from src/legacy/core_plugins/interpreter/public/functions/vis_dimension.js rename to src/legacy/core_plugins/interpreter/public/functions/vis_dimension.ts index e1a6c41198bad..19503dbe03ae9 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/vis_dimension.js +++ b/src/legacy/core_plugins/interpreter/public/functions/vis_dimension.ts @@ -22,48 +22,48 @@ import { i18n } from '@kbn/i18n'; export const visDimension = () => ({ name: 'visdimension', help: i18n.translate('interpreter.function.visDimension.help', { - defaultMessage: 'Generates visConfig dimension object' + defaultMessage: 'Generates visConfig dimension object', }), type: 'vis_dimension', context: { - types: [ - 'kibana_datatable' - ], + types: ['kibana_datatable'], }, args: { accessor: { types: ['string', 'number'], aliases: ['_'], help: i18n.translate('interpreter.function.visDimension.accessor.help', { - defaultMessage: 'Column in your dataset to use (either column index or column name)' + defaultMessage: 'Column in your dataset to use (either column index or column name)', }), }, format: { types: ['string'], - default: 'string' + default: 'string', }, formatParams: { types: ['string'], default: '"{}"', - } + }, }, - fn: (context, args) => { - const accessor = Number.isInteger(args.accessor) ? - args.accessor : - context.columns.find(c => c.id === args.accessor); + fn: (context: any, args: any) => { + const accessor = Number.isInteger(args.accessor) + ? args.accessor + : context.columns.find((c: any) => c.id === args.accessor); if (accessor === undefined) { - throw new Error(i18n.translate('interpreter.function.visDimension.error.accessor', { - defaultMessage: 'Column name provided is invalid' - })); + throw new Error( + i18n.translate('interpreter.function.visDimension.error.accessor', { + defaultMessage: 'Column name provided is invalid', + }) + ); } return { type: 'vis_dimension', - accessor: accessor, + accessor, format: { id: args.format, params: JSON.parse(args.formatParams), - } + }, }; }, }); diff --git a/src/legacy/core_plugins/interpreter/public/functions/visualization.js b/src/legacy/core_plugins/interpreter/public/functions/visualization.ts similarity index 91% rename from src/legacy/core_plugins/interpreter/public/functions/visualization.js rename to src/legacy/core_plugins/interpreter/public/functions/visualization.ts index 7dceeaf684354..94be78befd3d0 100644 --- a/src/legacy/core_plugins/interpreter/public/functions/visualization.js +++ b/src/legacy/core_plugins/interpreter/public/functions/visualization.ts @@ -20,17 +20,16 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; -import { setup as data } from '../../../data/public/legacy'; -import { start as visualizations } from '../../../visualizations/public/legacy'; - import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; import { PersistedState } from 'ui/persisted_state'; +import { setup as data } from '../../../data/public/legacy'; +import { start as visualizations } from '../../../visualizations/public/legacy'; export const visualization = () => ({ name: 'visualization', type: 'render', help: i18n.translate('interpreter.functions.visualization.help', { - defaultMessage: 'A simple visualization' + defaultMessage: 'A simple visualization', }), args: { index: { @@ -60,17 +59,17 @@ export const visualization = () => ({ uiState: { types: ['string'], default: '"{}"', - } + }, }, - async fn(context, args, handlers) { + async fn(context: any, args: any, handlers: any) { const $injector = await chrome.dangerouslyGetActiveInjector(); - const Private = $injector.get('Private'); + const Private = $injector.get('Private') as any; const { indexPatterns } = data.indexPatterns; const queryFilter = Private(FilterBarQueryFilterProvider); const visConfigParams = JSON.parse(args.visConfig); const schemas = JSON.parse(args.schemas); - const visType = visualizations.types.get(args.type || 'histogram'); + const visType = visualizations.types.get(args.type || 'histogram') as any; const indexPattern = args.index ? await indexPatterns.get(args.index) : null; const uiStateParams = JSON.parse(args.uiState); @@ -85,7 +84,7 @@ export const visualization = () => ({ timeRange: get(context, 'timeRange', null), query: get(context, 'query', null), filters: get(context, 'filters', null), - uiState: uiState, + uiState, inspectorAdapters: handlers.inspectorAdapters, queryFilter, forceFetch: true, @@ -95,14 +94,14 @@ export const visualization = () => ({ if (typeof visType.responseHandler === 'function') { if (context.columns) { // assign schemas to aggConfigs - context.columns.forEach(column => { + context.columns.forEach((column: any) => { if (column.aggConfig) { column.aggConfig.aggConfigs.schemas = visType.schemas.all; } }); Object.keys(schemas).forEach(key => { - schemas[key].forEach(i => { + schemas[key].forEach((i: any) => { if (context.columns[i] && context.columns[i].aggConfig) { context.columns[i].aggConfig.schema = key; } @@ -119,8 +118,8 @@ export const visualization = () => ({ value: { visData: context, visType: args.type, - visConfig: visConfigParams - } + visConfig: visConfigParams, + }, }; - } + }, }); diff --git a/src/legacy/core_plugins/interpreter/public/interpreter.test.js b/src/legacy/core_plugins/interpreter/public/interpreter.test.ts similarity index 96% rename from src/legacy/core_plugins/interpreter/public/interpreter.test.js rename to src/legacy/core_plugins/interpreter/public/interpreter.test.ts index bd7dc0a47c124..1de1e8c0cc059 100644 --- a/src/legacy/core_plugins/interpreter/public/interpreter.test.js +++ b/src/legacy/core_plugins/interpreter/public/interpreter.test.ts @@ -24,9 +24,9 @@ jest.mock('ui/new_platform', () => ({ injectedMetadata: { getKibanaVersion: () => '8.0.0', getBasePath: () => '/lol', - } - } - } + }, + }, + }, })); jest.mock('uiExports/interpreter'); @@ -38,7 +38,7 @@ jest.mock('@kbn/interpreter/common', () => ({ const mockInterpreter = { interpreter: { interpretAst: jest.fn(), - } + }, }; jest.mock('./lib/interpreter', () => ({ initializeInterpreter: jest.fn().mockReturnValue(Promise.resolve(mockInterpreter)), @@ -57,9 +57,9 @@ jest.mock('./functions', () => ({ functions: [{}, {}, {}] })); jest.mock('./renderers/visualization', () => ({ visualization: {} })); describe('interpreter/interpreter', () => { - let getInterpreter; - let interpretAst; - let initializeInterpreter; + let getInterpreter: any; + let interpretAst: any; + let initializeInterpreter: any; beforeEach(() => { jest.clearAllMocks(); @@ -117,5 +117,4 @@ describe('interpreter/interpreter', () => { expect(mockInterpreter.interpreter.interpretAst).toHaveBeenCalledTimes(2); }); }); - }); diff --git a/src/legacy/core_plugins/interpreter/public/interpreter.js b/src/legacy/core_plugins/interpreter/public/interpreter.ts similarity index 87% rename from src/legacy/core_plugins/interpreter/public/interpreter.js rename to src/legacy/core_plugins/interpreter/public/interpreter.ts index 84e05bb10d9fa..8ba82d5daf759 100644 --- a/src/legacy/core_plugins/interpreter/public/interpreter.js +++ b/src/legacy/core_plugins/interpreter/public/interpreter.ts @@ -18,6 +18,7 @@ */ import 'uiExports/interpreter'; +// @ts-ignore import { register, registryFactory } from '@kbn/interpreter/common'; import { initializeInterpreter } from './lib/interpreter'; import { registries } from './registries'; @@ -27,7 +28,10 @@ import { typeSpecs } from '../../../../plugins/expressions/common'; // Expose kbnInterpreter.register(specs) and kbnInterpreter.registries() globally so that plugins // can register without a transpile step. -global.kbnInterpreter = Object.assign(global.kbnInterpreter || {}, registryFactory(registries)); +(global as any).kbnInterpreter = Object.assign( + (global as any).kbnInterpreter || {}, + registryFactory(registries) +); register(registries, { types: typeSpecs, @@ -35,7 +39,7 @@ register(registries, { renderers: [visualization], }); -let interpreterPromise; +let interpreterPromise: Promise | undefined; export const getInterpreter = async () => { if (!interpreterPromise) { @@ -44,7 +48,7 @@ export const getInterpreter = async () => { return await interpreterPromise; }; -export const interpretAst = async (...params) => { +export const interpretAst = async (...params: any) => { const { interpreter } = await getInterpreter(); return await interpreter.interpretAst(...params); }; diff --git a/src/legacy/core_plugins/interpreter/public/lib/render_function.js b/src/legacy/core_plugins/interpreter/public/lib/render_function.ts similarity index 92% rename from src/legacy/core_plugins/interpreter/public/lib/render_function.js rename to src/legacy/core_plugins/interpreter/public/lib/render_function.ts index 04aa05951be70..76d1f58b66195 100644 --- a/src/legacy/core_plugins/interpreter/public/lib/render_function.js +++ b/src/legacy/core_plugins/interpreter/public/lib/render_function.ts @@ -17,7 +17,7 @@ * under the License. */ -export function RenderFunction(config) { +export function RenderFunction(this: any, config: any) { // This must match the name of the function that is used to create the `type: render` object this.name = config.name; @@ -36,7 +36,7 @@ export function RenderFunction(config) { // the function called to render the data this.render = config.render || - function render(domNode, data, done) { + function render(domNode: any, data: any, done: any) { done(); }; } diff --git a/src/legacy/core_plugins/interpreter/public/lib/render_functions_registry.js b/src/legacy/core_plugins/interpreter/public/lib/render_functions_registry.ts similarity index 88% rename from src/legacy/core_plugins/interpreter/public/lib/render_functions_registry.js rename to src/legacy/core_plugins/interpreter/public/lib/render_functions_registry.ts index 60e823baf0fa7..427e7f7454c24 100644 --- a/src/legacy/core_plugins/interpreter/public/lib/render_functions_registry.js +++ b/src/legacy/core_plugins/interpreter/public/lib/render_functions_registry.ts @@ -20,9 +20,9 @@ import { Registry } from '@kbn/interpreter/common'; import { RenderFunction } from './render_function'; -class RenderFunctionsRegistry extends Registry { - wrapper(obj) { - return new RenderFunction(obj); +class RenderFunctionsRegistry extends Registry { + wrapper(obj: any) { + return new (RenderFunction as any)(obj); } } diff --git a/src/legacy/core_plugins/interpreter/public/renderers/visualization.js b/src/legacy/core_plugins/interpreter/public/renderers/visualization.ts similarity index 84% rename from src/legacy/core_plugins/interpreter/public/renderers/visualization.js rename to src/legacy/core_plugins/interpreter/public/renderers/visualization.ts index 38fe02436380c..960e925b13221 100644 --- a/src/legacy/core_plugins/interpreter/public/renderers/visualization.js +++ b/src/legacy/core_plugins/interpreter/public/renderers/visualization.ts @@ -19,17 +19,18 @@ import chrome from 'ui/chrome'; import { visualizationLoader } from 'ui/visualize/loader/visualization_loader'; +// @ts-ignore import { VisProvider } from 'ui/visualize/loader/vis'; export const visualization = () => ({ name: 'visualization', displayName: 'visualization', reuseDomNode: true, - render: async (domNode, config, handlers) => { + render: async (domNode: HTMLElement, config: any, handlers: any) => { const { visData, visConfig, params } = config; const visType = config.visType || visConfig.type; const $injector = await chrome.dangerouslyGetActiveInjector(); - const Private = $injector.get('Private'); + const Private = $injector.get('Private') as any; const Vis = Private(VisProvider); if (handlers.vis) { @@ -49,8 +50,10 @@ export const visualization = () => ({ handlers.onDestroy(() => visualizationLoader.destroy()); - await visualizationLoader.render(domNode, handlers.vis, visData, handlers.vis.params, uiState, params).then(() => { - if (handlers.done) handlers.done(); - }); + await visualizationLoader + .render(domNode, handlers.vis, visData, handlers.vis.params, uiState, params) + .then(() => { + if (handlers.done) handlers.done(); + }); }, }); diff --git a/src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.js b/src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.ts similarity index 95% rename from src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.js rename to src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.ts index a6e0e13049e1c..0088663080774 100644 --- a/src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.js +++ b/src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.ts @@ -28,13 +28,13 @@ const mockServer = { plugins: { elasticsearch: { getCluster: () => ({ - callWithRequest: (...args) => Promise.resolve(args), + callWithRequest: (...args: any) => Promise.resolve(args), }), }, }, config: () => ({ has: () => false, - get: val => val, + get: (val: any) => val, }), info: { uri: 'serveruri', diff --git a/src/legacy/core_plugins/interpreter/server/lib/create_handlers.js b/src/legacy/core_plugins/interpreter/server/lib/create_handlers.ts similarity index 88% rename from src/legacy/core_plugins/interpreter/server/lib/create_handlers.js rename to src/legacy/core_plugins/interpreter/server/lib/create_handlers.ts index d4ea9b3dc6180..6e295d0aecaa5 100644 --- a/src/legacy/core_plugins/interpreter/server/lib/create_handlers.js +++ b/src/legacy/core_plugins/interpreter/server/lib/create_handlers.ts @@ -17,7 +17,7 @@ * under the License. */ -export const createHandlers = (request, server) => { +export const createHandlers = (request: any, server: any) => { const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); const config = server.config(); @@ -27,6 +27,6 @@ export const createHandlers = (request, server) => { config.has('server.rewriteBasePath') && config.get('server.rewriteBasePath') ? `${server.info.uri}${config.get('server.basePath')}` : server.info.uri, - elasticsearchClient: async (...args) => callWithRequest(request, ...args), + elasticsearchClient: async (...args: any) => callWithRequest(request, ...args), }; }; diff --git a/src/legacy/core_plugins/interpreter/server/routes/index.js b/src/legacy/core_plugins/interpreter/server/routes/index.ts similarity index 95% rename from src/legacy/core_plugins/interpreter/server/routes/index.js rename to src/legacy/core_plugins/interpreter/server/routes/index.ts index 9140f93a9bde6..50385147dd38e 100644 --- a/src/legacy/core_plugins/interpreter/server/routes/index.js +++ b/src/legacy/core_plugins/interpreter/server/routes/index.ts @@ -19,6 +19,6 @@ import { registerServerFunctions } from './server_functions'; -export function routes(server) { +export function routes(server: any) { registerServerFunctions(server); } diff --git a/src/legacy/core_plugins/interpreter/server/routes/server_functions.js b/src/legacy/core_plugins/interpreter/server/routes/server_functions.ts similarity index 84% rename from src/legacy/core_plugins/interpreter/server/routes/server_functions.js rename to src/legacy/core_plugins/interpreter/server/routes/server_functions.ts index b64a9af006e41..740b046610d9e 100644 --- a/src/legacy/core_plugins/interpreter/server/routes/server_functions.js +++ b/src/legacy/core_plugins/interpreter/server/routes/server_functions.ts @@ -18,16 +18,16 @@ */ import Boom from 'boom'; +import Joi from 'joi'; import { serializeProvider, API_ROUTE } from '../../common'; import { createHandlers } from '../lib/create_handlers'; -import Joi from 'joi'; /** * Register the Canvas function endopints. * * @param {*} server - The Kibana server */ -export function registerServerFunctions(server) { +export function registerServerFunctions(server: any) { getServerFunctions(server); runServerFunctions(server); } @@ -37,7 +37,7 @@ export function registerServerFunctions(server) { * * @param {*} server - The Kibana server */ -function runServerFunctions(server) { +function runServerFunctions(server: any) { server.route({ method: 'POST', path: `${API_ROUTE}/fns`, @@ -48,19 +48,20 @@ function runServerFunctions(server) { }, validate: { payload: Joi.object({ - functions: Joi.array().items( - Joi.object() - .keys({ + functions: Joi.array() + .items( + Joi.object().keys({ id: Joi.number().required(), functionName: Joi.string().required(), args: Joi.object().default({}), context: Joi.any().default(null), - }), - ).required(), + }) + ) + .required(), }).required(), }, }, - async handler(req) { + async handler(req: any) { const handlers = await createHandlers(req, server); const { functions } = req.payload; @@ -73,19 +74,19 @@ function runServerFunctions(server) { // Send the initial headers. res.writeHead(200, { 'Content-Type': 'text/plain', - 'Connection': 'keep-alive', + Connection: 'keep-alive', 'Transfer-Encoding': 'chunked', 'Cache-Control': 'no-cache', }); // Write a length-delimited response - const streamResult = (result) => { + const streamResult = (result: any) => { const payload = JSON.stringify(result) + '\n'; res.write(`${payload.length}:${payload}`); }; // Tries to run an interpreter function, and ensures a consistent error payload on failure. - const tryFunction = async (id, fnCall) => { + const tryFunction = async (id: any, fnCall: any) => { try { const result = await runFunction(server, handlers, fnCall); @@ -96,7 +97,7 @@ function runServerFunctions(server) { return { id, statusCode: 200, result }; } catch (err) { if (Boom.isBoom(err)) { - return batchError(id, err.output.payload, err.statusCode); + return batchError(id, err.output.payload, (err as any).statusCode); } else if (err instanceof Error) { return batchError(id, err.message); } @@ -107,7 +108,9 @@ function runServerFunctions(server) { }; // Process each function individually, and stream the responses back to the client - await Promise.all(functions.map(({ id, ...fnCall }) => tryFunction(id, fnCall).then(streamResult))); + await Promise.all( + functions.map(({ id, ...fnCall }: any) => tryFunction(id, fnCall).then(streamResult)) + ); // All of the responses have been written, so we can close the response. res.end(); @@ -118,7 +121,7 @@ function runServerFunctions(server) { /** * A helper function for bundling up errors. */ -function batchError(id, message, statusCode = 500) { +function batchError(id: any, message: any, statusCode = 500) { return { id, statusCode, @@ -130,7 +133,7 @@ function batchError(id, message, statusCode = 500) { * Register the endpoint that returns the list of server-only functions. * @param {*} server - The Kibana server */ -function getServerFunctions(server) { +function getServerFunctions(server: any) { server.route({ method: 'GET', path: `${API_ROUTE}/fns`, @@ -147,7 +150,7 @@ function getServerFunctions(server) { * @param {*} handlers - The Canvas handlers * @param {*} fnCall - Describes the function being run `{ functionName, args, context }` */ -async function runFunction(server, handlers, fnCall) { +async function runFunction(server: any, handlers: any, fnCall: any) { const registries = server.plugins.interpreter.registries(); const { functionName, args, context } = fnCall; const types = registries.types.toJS(); diff --git a/src/legacy/core_plugins/interpreter/test_helpers.js b/src/legacy/core_plugins/interpreter/test_helpers.ts similarity index 86% rename from src/legacy/core_plugins/interpreter/test_helpers.js rename to src/legacy/core_plugins/interpreter/test_helpers.ts index e743b8a09280e..741cd83bb47fe 100644 --- a/src/legacy/core_plugins/interpreter/test_helpers.js +++ b/src/legacy/core_plugins/interpreter/test_helpers.ts @@ -21,8 +21,9 @@ import { mapValues } from 'lodash'; // Takes a function spec and passes in default args, // overriding with any provided args. -export const functionWrapper = fnSpec => { +export const functionWrapper = (fnSpec: any) => { const spec = fnSpec(); const defaultArgs = mapValues(spec.args, argSpec => argSpec.default); - return (context, args, handlers) => spec.fn(context, { ...defaultArgs, ...args }, handlers); + return (context: any, args: any, handlers: any) => + spec.fn(context, { ...defaultArgs, ...args }, handlers); }; diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.test.ts b/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.test.ts index 28021a763b287..009797905701c 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.test.ts +++ b/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.test.ts @@ -30,7 +30,7 @@ describe('interpreter/functions#markdown', () => { }; it('returns an object with the correct structure', async () => { - const actual = await fn(undefined, args); + const actual = await fn(undefined, args, undefined); expect(actual).toMatchSnapshot(); }); }); diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts index 5fe2ac7b7fdf0..fee6dec641842 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts @@ -67,7 +67,7 @@ describe('interpreter/functions#metric', () => { }; it('returns an object with the correct structure', () => { - const actual = fn(context, args); + const actual = fn(context, args, undefined); expect(actual).toMatchSnapshot(); }); diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts index 2bdebc8a9d19e..1c1b808ffb014 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts +++ b/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts @@ -76,12 +76,12 @@ describe('interpreter/functions#table', () => { }); it('returns an object with the correct structure', async () => { - const actual = await fn(context, { visConfig: JSON.stringify(visConfig) }); + const actual = await fn(context, { visConfig: JSON.stringify(visConfig) }, undefined); expect(actual).toMatchSnapshot(); }); it('calls response handler with correct values', async () => { - await fn(context, { visConfig: JSON.stringify(visConfig) }); + await fn(context, { visConfig: JSON.stringify(visConfig) }, undefined); expect(mockResponseHandler).toHaveBeenCalledTimes(1); expect(mockResponseHandler).toHaveBeenCalledWith(context, visConfig.dimensions); }); diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts index d14871c6bd337..0365f7840cac4 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts @@ -39,7 +39,7 @@ describe('interpreter/functions#tagcloud', () => { }; it('returns an object with the correct structure', () => { - const actual = fn(context, visConfig); + const actual = fn(context, visConfig, undefined); expect(actual).toMatchSnapshot(); }); }); From e322acceab83c444843447f0e55dc3c798d155e6 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 3 Oct 2019 13:57:31 +0500 Subject: [PATCH 03/35] [Uptime] Change default status filter in ping list to all on monitor page (#47108) * change default status filter in ping list to all * update snaps --- .../plugins/uptime/common/constants/client_defaults.ts | 2 +- .../__tests__/__snapshots__/use_url_params.test.tsx.snap | 2 +- .../__snapshots__/get_supported_url_params.test.ts.snap | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts b/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts index 21e866e991944..66ac571e2b7a5 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts @@ -31,6 +31,6 @@ export const CLIENT_DEFAULTS = { MONITOR_LIST_SORT_DIRECTION: 'asc', MONITOR_LIST_SORT_FIELD: 'monitor_id', SEARCH: '', - SELECTED_PING_LIST_STATUS: 'down', + SELECTED_PING_LIST_STATUS: '', STATUS_FILTER: '', }; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap index 4b45ed20d5d83..5794169d75597 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap @@ -5,7 +5,7 @@ exports[`useUrlParams gets the expected values using the context 1`] = ` hook={[Function]} >
- {"absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-19d","dateRangeEnd":"now-1m","filters":"","search":"","selectedPingStatus":"down","statusFilter":""} + {"absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-19d","dateRangeEnd":"now-1m","filters":"","search":"","selectedPingStatus":"","statusFilter":""}
- - diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job/wizard/steps/index_or_search/paginated_selectable_list/paginated_selectable_list.js b/x-pack/legacy/plugins/ml/public/jobs/new_job/wizard/steps/index_or_search/paginated_selectable_list/paginated_selectable_list.js deleted file mode 100644 index 784bf84322dd3..0000000000000 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job/wizard/steps/index_or_search/paginated_selectable_list/paginated_selectable_list.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - -import _ from 'lodash'; -import { uiModules } from 'ui/modules'; -import 'ui/directives/paginate'; -import 'ui/directives/kbn_href'; -import paginatedSelectableListTemplate from './paginated_selectable_list.html'; - -const module = uiModules.get('kibana'); - -function throwError(message) { - throw new Error(message); -} - -module.directive('paginatedSelectableList', function () { - - return { - restrict: 'E', - scope: { - perPage: '=?', - list: '=', - listProperty: '@', - userMakeUrl: '=?', - userOnSelect: '=?', - disableAutoFocus: '=' - }, - template: paginatedSelectableListTemplate, - controller: function ($scope, $filter) { - function calculateHitsByQuery() { - $scope.hitsByQuery = $filter('filter')($scope.hits, $scope.query); - } - - // Should specify either user-make-url or user-on-select - if (!$scope.userMakeUrl && !$scope.userOnSelect) { - throwError('paginatedSelectableList directive expects a makeUrl or onSelect function'); - } - - // Should specify either user-make-url or user-on-select, but not both. - if ($scope.userMakeUrl && $scope.userOnSelect) { - throwError('paginatedSelectableList directive expects a makeUrl or onSelect attribute but not both'); - } - - $scope.perPage = $scope.perPage || 10; - $scope.hits = $scope.list = _.sortBy($scope.list, $scope.accessor); - $scope.$watchGroup(['hits', 'query'], calculateHitsByQuery); - calculateHitsByQuery(); - $scope.hitCount = $scope.hits.length; - - /** - * Boolean that keeps track of whether hits are sorted ascending (true) - * or descending (false) - * * @type {Boolean} - */ - $scope.isAscending = true; - - /** - * Sorts saved object finder hits either ascending or descending - * @param {Array} hits Array of saved finder object hits - * @return {Array} Array sorted either ascending or descending - */ - $scope.sortHits = function (hits) { - const sortedList = _.sortBy(hits, $scope.accessor); - - $scope.isAscending = !$scope.isAscending; - $scope.hits = $scope.isAscending ? sortedList : sortedList.reverse(); - }; - - $scope.makeUrl = function (hit) { - return $scope.userMakeUrl(hit); - }; - - $scope.onSelect = function (hit, $event) { - return $scope.userOnSelect(hit, $event); - }; - - $scope.accessor = function (val) { - const prop = $scope.listProperty; - return prop ? _.get(val, prop) : val; - }; - } - }; -}); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/index.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/index.ts index 2366f2c655000..f0061b1b9847e 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/index.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/index.ts @@ -8,3 +8,5 @@ import './pages/new_job/route'; import './pages/new_job/directive'; import './pages/job_type/route'; import './pages/job_type/directive'; +import './pages/index_or_search/route'; +import './pages/index_or_search/directive'; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/__test__/directive.js b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/__test__/directive.js new file mode 100644 index 0000000000000..bd63a16abfacd --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/__test__/directive.js @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import ngMock from 'ng_mock'; +import expect from '@kbn/expect'; +import sinon from 'sinon'; + +// Import this way to be able to stub/mock functions later on in the tests using sinon. +import * as indexUtils from 'plugins/ml/util/index_utils'; + +describe('ML - Index or Saved Search selection directive', () => { + let $scope; + let $compile; + let $element; + + beforeEach(ngMock.module('kibana')); + beforeEach(() => { + ngMock.inject(function ($injector) { + $compile = $injector.get('$compile'); + const $rootScope = $injector.get('$rootScope'); + $scope = $rootScope.$new(); + }); + }); + + afterEach(() => { + $scope.$destroy(); + }); + + it('Initialize Index or Saved Search selection directive', done => { + sinon.stub(indexUtils, 'timeBasedIndexCheck').callsFake(() => false); + ngMock.inject(function () { + expect(() => { + $element = $compile('')($scope); + }).to.not.throwError(); + + // directive has scope: false + const scope = $element.isolateScope(); + expect(scope).to.eql(undefined); + done(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/directive.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/directive.tsx new file mode 100644 index 0000000000000..7f3edf0896840 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/directive.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; + +// @ts-ignore +import { uiModules } from 'ui/modules'; +const module = uiModules.get('apps/ml', ['react']); +import { timefilter } from 'ui/timefilter'; + +import { I18nContext } from 'ui/i18n'; +import { InjectorService } from '../../../../../common/types/angular'; +import { Page } from './page'; + +module.directive('mlIndexOrSearch', ($injector: InjectorService) => { + return { + scope: {}, + restrict: 'E', + link: async (scope: ng.IScope, element: ng.IAugmentedJQuery) => { + // remove time picker from top of page + timefilter.disableTimeRangeSelector(); + timefilter.disableAutoRefreshSelector(); + + const $route = $injector.get('$route'); + const { nextStepPath } = $route.current.locals; + + ReactDOM.render( + {React.createElement(Page, { nextStepPath })}, + element[0] + ); + + element.on('$destroy', () => { + ReactDOM.unmountComponentAtNode(element[0]); + scope.$destroy(); + }); + }, + }; +}); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/page.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/page.tsx new file mode 100644 index 0000000000000..68013bd243a91 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/page.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC } from 'react'; +import { + EuiPage, + EuiPageBody, + EuiTitle, + EuiPageHeader, + EuiPageHeaderSection, + EuiPageContent, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; + +export interface PageProps { + nextStepPath: string; +} + +export const Page: FC = ({ nextStepPath }) => { + const RESULTS_PER_PAGE = 20; + + const onObjectSelection = (id: string, type: string) => { + window.location.href = `${nextStepPath}?${ + type === 'index-pattern' ? 'index' : 'savedSearchId' + }=${encodeURIComponent(id)}`; + }; + + return ( + + + + + +

+ +

+
+
+
+ + 'search', + name: i18n.translate( + 'xpack.ml.newJob.wizard.searchSelection.savedObjectType.search', + { + defaultMessage: 'Saved search', + } + ), + }, + { + type: 'index-pattern', + getIconForSavedObject: () => 'indexPatternApp', + name: i18n.translate( + 'xpack.ml.newJob.wizard.searchSelection.savedObjectType.indexPattern', + { + defaultMessage: 'Index pattern', + } + ), + }, + ]} + fixedPageSize={RESULTS_PER_PAGE} + /> + +
+
+ ); +}; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/route.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/route.ts new file mode 100644 index 0000000000000..1ed12577960c3 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/index_or_search/route.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// @ts-ignore +import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes'; +// @ts-ignore +import { preConfiguredJobRedirect } from 'plugins/ml/jobs/new_job/wizard/preconfigured_job_redirect'; +import uiRoutes from 'ui/routes'; +// @ts-ignore +import { checkLicenseExpired, checkBasicLicense } from '../../../../license/check_license'; +import { loadIndexPatterns } from '../../../../util/index_utils'; +import { + checkCreateJobsPrivilege, + checkFindFileStructurePrivilege, +} from '../../../../privilege/check_privilege'; +import { + getCreateJobBreadcrumbs, + getDataVisualizerIndexOrSearchBreadcrumbs, +} from '../../../breadcrumbs'; + +uiRoutes.when('/jobs/new_job', { + redirectTo: '/jobs/new_job/step/index_or_search', +}); + +uiRoutes.when('/jobs/new_job/step/index_or_search', { + template: '', + k7Breadcrumbs: getCreateJobBreadcrumbs, + resolve: { + CheckLicense: checkLicenseExpired, + privileges: checkCreateJobsPrivilege, + indexPatterns: loadIndexPatterns, + preConfiguredJobRedirect, + checkMlNodesAvailable, + nextStepPath: () => '#/jobs/new_job/step/job_type', + }, +}); + +uiRoutes.when('/datavisualizer_index_select', { + template: '', + k7Breadcrumbs: getDataVisualizerIndexOrSearchBreadcrumbs, + resolve: { + CheckLicense: checkBasicLicense, + privileges: checkFindFileStructurePrivilege, + indexPatterns: loadIndexPatterns, + nextStepPath: () => '#jobs/new_job/datavisualizer', + }, +}); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 97b509f0e3ffe..d055e77606587 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7175,8 +7175,6 @@ "xpack.ml.newJob.simple.watcher.email.timeLabel": "時間", "xpack.ml.newJob.simple.watcher.email.topInfluencersLabel": "トップ影響因子:", "xpack.ml.newJob.simple.watcher.email.topRecordsLabel": "トップの記録:", - "xpack.ml.newJob.wizard.createFromNewSearchTitle": "新規検索からインデックスを選択", - "xpack.ml.newJob.wizard.createFromSavedSearchTitle": "または保存された検索から", "xpack.ml.newJob.wizard.jobType.advancedAriaLabel": "高度なジョブ", "xpack.ml.newJob.wizard.jobType.advancedDescription": "より高度なユースケースでは、ジョブの作成にすべてのオプションを使用します。", "xpack.ml.newJob.wizard.jobType.advancedTitle": "高度な設定", @@ -7205,7 +7203,6 @@ "xpack.ml.newJob.wizard.jobType.useSuppliedConfigurationTitle": "提供された構成を使用", "xpack.ml.newJob.wizard.jobType.useWizardDescription": "ウィザードの 1 つを使用し、データの異常を検知する機械学習ジョブを作成します。", "xpack.ml.newJob.wizard.jobType.useWizardTitle": "ウィザードを使用", - "xpack.ml.newJob.wizard.savedSearchesTooltip": "保存された検索", "xpack.ml.privilege.licenseHasExpiredTooltip": "ご使用のライセンスは期限切れです。", "xpack.ml.privilege.noPermission.createCalendarsTooltip": "カレンダーを作成するパーミッションがありません。", "xpack.ml.privilege.noPermission.createDataFrameTransformTooltip": "データフレーム変換を作成するパーミッションがありません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index aaced82cb5b14..6ca82b053ba66 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7177,8 +7177,6 @@ "xpack.ml.newJob.simple.watcher.email.timeLabel": "时间", "xpack.ml.newJob.simple.watcher.email.topInfluencersLabel": "排在前面的影响因素:", "xpack.ml.newJob.simple.watcher.email.topRecordsLabel": "排在前面的记录:", - "xpack.ml.newJob.wizard.createFromNewSearchTitle": "基于“新搜索”,选择“索引”", - "xpack.ml.newJob.wizard.createFromSavedSearchTitle": "或者,基于“已保存的搜索”", "xpack.ml.newJob.wizard.jobType.advancedAriaLabel": "高级作业", "xpack.ml.newJob.wizard.jobType.advancedDescription": "使用全部选项为更高级的用例创建作业。", "xpack.ml.newJob.wizard.jobType.advancedTitle": "高级", @@ -7207,7 +7205,6 @@ "xpack.ml.newJob.wizard.jobType.useSuppliedConfigurationTitle": "使用提供的配置", "xpack.ml.newJob.wizard.jobType.useWizardDescription": "使用其中一个向导创建 Machine Learning 作业,以查找数据中的异常。", "xpack.ml.newJob.wizard.jobType.useWizardTitle": "使用向导", - "xpack.ml.newJob.wizard.savedSearchesTooltip": "已保存的搜索", "xpack.ml.privilege.licenseHasExpiredTooltip": "您的许可证已过期。", "xpack.ml.privilege.noPermission.createCalendarsTooltip": "您没有权限创建日历。", "xpack.ml.privilege.noPermission.createDataFrameTransformTooltip": "您无权创建数据帧转换。", diff --git a/x-pack/test/functional/services/machine_learning/job_source_selection.ts b/x-pack/test/functional/services/machine_learning/job_source_selection.ts index 89fae06fd33ce..81f2de3a47e79 100644 --- a/x-pack/test/functional/services/machine_learning/job_source_selection.ts +++ b/x-pack/test/functional/services/machine_learning/job_source_selection.ts @@ -11,8 +11,7 @@ export function MachineLearningJobSourceSelectionProvider({ getService }: FtrPro return { async selectSourceIndexPattern(indexPattern: string) { - const subj = 'paginatedListItem-' + indexPattern; - await testSubjects.clickWhenNotDisabled(subj); + await testSubjects.clickWhenNotDisabled(`savedObjectTitle${indexPattern}`); await testSubjects.existOrFail('mlPageJobTypeSelection'); }, }; From 127eab03201f5805c789ab9976560afb9b316737 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 3 Oct 2019 11:13:42 +0200 Subject: [PATCH 05/35] [Graph] Empty workspace overlay (#45547) --- .../public/angular/graph_client_workspace.js | 9 +- .../graph/public/angular/templates/index.html | 4 +- x-pack/legacy/plugins/graph/public/app.js | 30 +++- .../graph/public/components/_index.scss | 2 + .../public/components/_source_modal.scss | 4 + .../plugins/graph/public/components/app.tsx | 63 +++++--- .../components/field_manager/field_icon.tsx | 2 +- .../field_manager/field_manager.test.tsx | 27 +++- .../field_manager/field_manager.tsx | 30 ++-- .../components/field_manager/field_picker.tsx | 12 +- .../guidance_panel/_guidance_panel.scss | 47 ++++++ .../components/guidance_panel/_index.scss | 1 + .../guidance_panel/guidance_panel.tsx | 143 ++++++++++++++++++ .../public/components/guidance_panel/index.ts | 7 + .../public/components/search_bar.test.tsx | 3 + .../graph/public/components/search_bar.tsx | 117 +++++++------- .../graph/public/components/source_modal.tsx | 4 +- .../public/services/fetch_top_nodes.test.ts | 97 ++++++++++++ .../graph/public/services/fetch_top_nodes.ts | 112 ++++++++++++++ .../graph/public/types/workspace_state.ts | 42 ++--- 20 files changed, 622 insertions(+), 134 deletions(-) create mode 100644 x-pack/legacy/plugins/graph/public/components/_source_modal.scss create mode 100644 x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss create mode 100644 x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss create mode 100644 x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx create mode 100644 x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts create mode 100644 x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts create mode 100644 x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts diff --git a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js b/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js index db427fd11c765..96be9eed2b467 100644 --- a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js +++ b/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js @@ -1200,11 +1200,14 @@ module.exports = (function () { } - //Add missing links between existing nodes - this.fillInGraph = function () { + /** + * Add missing links between existing nodes + * @param maxNewEdges Max number of new edges added. Avoid adding too many new edges + * at once into the graph otherwise disorientating + */ + this.fillInGraph = function (maxNewEdges = 10) { let nodesForLinking = self.getSelectedOrAllTopNodes(); - const maxNewEdges = 10; // Avoid adding too many new edges at once into the graph otherwise disorientating const maxNumVerticesSearchable = 100; diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/legacy/plugins/graph/public/angular/templates/index.html index 3ed9b390c6a78..07b57ee322548 100644 --- a/x-pack/legacy/plugins/graph/public/angular/templates/index.html +++ b/x-pack/legacy/plugins/graph/public/angular/templates/index.html @@ -12,15 +12,17 @@ on-index-pattern-selected="uiSelectIndex" on-query-submit="submit" is-loading="loading" + is-initialized="!!workspace || savedWorkspace.id" initial-query="initialQuery" state="reduxState" dispatch="reduxDispatch" + on-fill-workspace="fillWorkspace" autocomplete-start="autocompleteStart" core-start="coreStart" store="store" > -
+
{ + try { + const fields = selectedFieldsSelector(store.getState()); + const topTermNodes = await fetchTopNodes( + npStart.core.http.post, + $scope.selectedIndex.title, + fields + ); + initWorkspaceIfRequired(); + $scope.workspace.mergeGraph({ + nodes: topTermNodes, + edges: [] + }); + $scope.workspace.fillInGraph(fields.length * 10); + } catch (e) { + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.graph.fillWorkspaceError', + { defaultMessage: 'Fetching top terms failed: {message}', values: { message: e.message } } + ), + }); + } + }; + $scope.submit = function (searchTerm) { initWorkspaceIfRequired(); const numHops = 2; @@ -846,9 +873,6 @@ app.controller('graphuiPlugin', function ( } else { $route.current.locals.SavedWorkspacesProvider.get().then(function (newWorkspace) { $scope.savedWorkspace = newWorkspace; - openSourceModal(npStart.core, indexPattern => { - $scope.indexSelected(indexPattern); - }); }); } diff --git a/x-pack/legacy/plugins/graph/public/components/_index.scss b/x-pack/legacy/plugins/graph/public/components/_index.scss index 85bbf4fcc3ade..a06209e7e4d34 100644 --- a/x-pack/legacy/plugins/graph/public/components/_index.scss +++ b/x-pack/legacy/plugins/graph/public/components/_index.scss @@ -1,5 +1,7 @@ @import './app'; @import './search_bar'; +@import './source_modal'; +@import './guidance_panel/index'; @import './graph_visualization/index'; @import './venn_diagram/index'; @import './settings/index'; diff --git a/x-pack/legacy/plugins/graph/public/components/_source_modal.scss b/x-pack/legacy/plugins/graph/public/components/_source_modal.scss new file mode 100644 index 0000000000000..fbc293442f331 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/_source_modal.scss @@ -0,0 +1,4 @@ +.gphSourceModal { + width: 720px; + min-height: 530px; +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/components/app.tsx b/x-pack/legacy/plugins/graph/public/components/app.tsx index 907e7e4cecdcd..894c6b9ef45ac 100644 --- a/x-pack/legacy/plugins/graph/public/components/app.tsx +++ b/x-pack/legacy/plugins/graph/public/components/app.tsx @@ -5,12 +5,16 @@ */ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; +import React, { useState } from 'react'; +import { I18nProvider } from '@kbn/i18n/react'; import { Storage } from 'ui/storage'; import { CoreStart } from 'kibana/public'; import { AutocompletePublicPluginStart } from 'src/plugins/data/public'; import { FieldManagerProps, FieldManager } from './field_manager'; import { SearchBarProps, SearchBar } from './search_bar'; +import { GuidancePanel } from './guidance_panel'; +import { selectedFieldsSelector } from '../state_management'; +import { openSourceModal } from '../services/source_modal'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; @@ -18,28 +22,47 @@ export interface GraphAppProps extends FieldManagerProps, SearchBarProps { coreStart: CoreStart; autocompleteStart: AutocompletePublicPluginStart; store: Storage; + onFillWorkspace: () => void; + isInitialized: boolean; } export function GraphApp(props: GraphAppProps) { + const [pickerOpen, setPickerOpen] = useState(false); + return ( - -
- - - - - - - - -
-
+ + +
+ + + + + + + + +
+ {!props.isInitialized && ( + 0} + onFillWorkspace={props.onFillWorkspace} + onOpenFieldPicker={() => { + setPickerOpen(true); + }} + onOpenDatasourcePicker={() => { + openSourceModal(props.coreStart, props.onIndexPatternSelected); + }} + /> + )} +
+
); } diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx index 93561e1722936..429eec19a47fa 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx @@ -16,7 +16,7 @@ function getIconForDataType(dataType: string) { boolean: 'invert', date: 'calendar', geo_point: 'globe', - ip: 'link', + ip: 'storage', }; return icons[dataType] || ICON_TYPES.find(t => t === dataType) || 'document'; } diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx index 32cc546a3ad0c..fb715e759c62d 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx @@ -18,6 +18,7 @@ describe('field_manager', () => { let store: GraphStore; let instance: ShallowWrapper; let dispatchSpy: jest.Mock; + let openSpy: jest.Mock; beforeEach(() => { store = createGraphStore(); @@ -52,8 +53,16 @@ describe('field_manager', () => { ); dispatchSpy = jest.fn(store.dispatch); - - instance = shallow(); + openSpy = jest.fn(); + + instance = shallow( + + ); }); function update() { @@ -80,13 +89,19 @@ describe('field_manager', () => { }); it('should select fields from picker', () => { - const fieldPicker = instance.find(FieldPicker).dive(); - act(() => { - (fieldPicker.find(EuiPopover).prop('button')! as ReactElement).props.onClick(); + (instance + .find(FieldPicker) + .dive() + .find(EuiPopover) + .prop('button')! as ReactElement).props.onClick(); }); - fieldPicker.update(); + expect(openSpy).toHaveBeenCalled(); + + instance.setProps({ pickerOpen: true }); + + const fieldPicker = instance.find(FieldPicker).dive(); expect( fieldPicker diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx index 7f89b555c9f7a..e44ad248e279d 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { I18nProvider } from '@kbn/i18n/react'; import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { bindActionCreators } from 'redux'; @@ -24,9 +23,11 @@ import { export interface FieldManagerProps { state: GraphState; dispatch: GraphDispatch; + pickerOpen: boolean; + setPickerOpen: (open: boolean) => void; } -export function FieldManager({ state, dispatch }: FieldManagerProps) { +export function FieldManager({ state, dispatch, pickerOpen, setPickerOpen }: FieldManagerProps) { const fieldMap = fieldMapSelector(state); const allFields = fieldsSelector(state); const selectedFields = selectedFieldsSelector(state); @@ -41,17 +42,20 @@ export function FieldManager({ state, dispatch }: FieldManagerProps) { ); return ( - - - {selectedFields.map(field => ( - - - - ))} - - + + {selectedFields.map(field => ( + + - - + ))} + + + + ); } diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx index b1ddce4fa1744..8ef566e881989 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx @@ -16,11 +16,17 @@ export interface FieldPickerProps { fieldMap: Record; selectField: (fieldName: string) => void; deselectField: (fieldName: string) => void; + open: boolean; + setOpen: (open: boolean) => void; } -export function FieldPicker({ fieldMap, selectField, deselectField }: FieldPickerProps) { - const [open, setOpen] = useState(false); - +export function FieldPicker({ + fieldMap, + selectField, + deselectField, + open, + setOpen, +}: FieldPickerProps) { const allFields = Object.values(fieldMap); const unselectedFields = allFields.filter(field => !field.selected); const hasSelectedFields = unselectedFields.length < allFields.length; diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss b/x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss new file mode 100644 index 0000000000000..f1c332eba1aa8 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss @@ -0,0 +1,47 @@ +.gphGuidancePanel { + max-width: 580px; + margin: $euiSizeL 0; +} + +.gphGuidancePanel__list { + list-style: none; + margin: 0; + padding: 0; +} + +.gphGuidancePanel__item { + display: block; + max-width: 420px; + position: relative; + padding-left: $euiSizeXL; + margin-bottom: $euiSizeL; + + button { + // make buttons wrap lines like regular text + display: contents; + } +} + +.gphGuidancePanel__item--disabled { + color: $euiColorDarkShade; + pointer-events: none; + + button { + color: $euiColorDarkShade !important; + } +} + +.gphGuidancePanel__itemIcon { + position: absolute; + left: 0; + top: -($euiSizeXS / 2); + width: $euiSizeL; + height: $euiSizeL; + padding: $euiSizeXS; + + &--done { + background-color: $euiColorSecondary; + color: $euiColorEmptyShade; + border-radius: 50%; + } +} diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss b/x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss new file mode 100644 index 0000000000000..65c71cc17ba35 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss @@ -0,0 +1 @@ +@import './_guidance_panel'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx new file mode 100644 index 0000000000000..62d8bbb03bc3f --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { ReactNode } from 'react'; +import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import classNames from 'classnames'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export interface GuidancePanelProps { + onFillWorkspace: () => void; + onOpenFieldPicker: () => void; + onOpenDatasourcePicker: () => void; + hasDatasource: boolean; + hasFields: boolean; +} + +function ListItem({ + children, + state, +}: { + state: 'done' | 'active' | 'disabled'; + children: ReactNode; +}) { + return ( +
  • + {state !== 'disabled' && ( + + + + )} + {children} +
  • + ); +} + +export function GuidancePanel(props: GuidancePanelProps) { + const { + onFillWorkspace, + onOpenFieldPicker, + onOpenDatasourcePicker, + hasDatasource, + hasFields, + } = props; + + return ( + + + + + + + + + +

    + {i18n.translate('xpack.graph.guidancePanel.title', { + defaultMessage: "Let's get started!", + })} +

    +
    +
    + +
      + + + {i18n.translate( + 'xpack.graph.guidancePanel.datasourceItem.indexPatternButtonLabel', + { + defaultMessage: 'index pattern', + } + )} + + ), + }} + /> + + + + {i18n.translate( + 'xpack.graph.guidancePanel.fieldsItem.fieldsButtonLabel', + { + defaultMessage: 'Select fields', + } + )} + + ), + }} + /> + + + + {i18n.translate( + 'xpack.graph.guidancePanel.nodesItem.topTermsButtonLabel', + { + defaultMessage: 'show correlations of the top terms', + } + )} + + ), + }} + /> + +
    +
    +
    +
    +
    +
    + ); +} diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts b/x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts new file mode 100644 index 0000000000000..8704eb2eb6761 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './guidance_panel'; diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx b/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx index 80b1c3c343942..dbad0e01078fd 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx @@ -51,6 +51,7 @@ describe('search_bar', () => { onIndexPatternSelected: () => {}, onQuerySubmit: querySubmit, currentIndexPattern: { title: 'Testpattern' } as IndexPattern, + coreStart: {} as CoreStart, }) ); act(() => { @@ -72,6 +73,7 @@ describe('search_bar', () => { onIndexPatternSelected: () => {}, onQuerySubmit: querySubmit, currentIndexPattern: { title: 'Testpattern', fields: [{ name: 'test' }] } as IndexPattern, + coreStart: {} as CoreStart, }) ); act(() => { @@ -97,6 +99,7 @@ describe('search_bar', () => { onIndexPatternSelected: indexPatternSelected, onQuerySubmit: () => {}, currentIndexPattern: { title: 'Testpattern' } as IndexPattern, + coreStart: {} as CoreStart, }) ); diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx b/x-pack/legacy/plugins/graph/public/components/search_bar.tsx index 226f6f829d8a4..18eca326776f5 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/legacy/plugins/graph/public/components/search_bar.tsx @@ -8,9 +8,9 @@ import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty, EuiToolTip } from import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { I18nProvider } from '@kbn/i18n/react'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { IDataPluginServices } from 'src/legacy/core_plugins/data/public/types'; +import { CoreStart } from 'kibana/public'; import { QueryBarInput, Query, @@ -21,6 +21,7 @@ import { openSourceModal } from '../services/source_modal'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; export interface SearchBarProps { + coreStart: CoreStart; isLoading: boolean; currentIndexPattern?: IndexPattern; initialQuery?: string; @@ -54,71 +55,61 @@ export function SearchBar(props: SearchBarProps) { } = props; const [query, setQuery] = useState({ language: 'kuery', query: initialQuery || '' }); const kibana = useKibana(); - const { overlays, uiSettings, savedObjects } = kibana.services; + const { overlays } = kibana.services; if (!overlays) return null; return ( - -
    { - e.preventDefault(); - if (!isLoading && currentIndexPattern) { - onQuerySubmit(queryToString(query, currentIndexPattern)); - } - }} - > - - - { + e.preventDefault(); + if (!isLoading && currentIndexPattern) { + onQuerySubmit(queryToString(query, currentIndexPattern)); + } + }} + > + + + + { + openSourceModal(props.coreStart, onIndexPatternSelected); + }} > - { - openSourceModal( - { - overlays, - savedObjects, - uiSettings, - }, - onIndexPatternSelected - ); - }} - > - {currentIndexPattern - ? currentIndexPattern.title - : // This branch will be shown if the user exits the - // initial picker modal - i18n.translate('xpack.graph.bar.pickSourceLabel', { - defaultMessage: 'Click here to pick a data source', - })} - - - } - onChange={setQuery} - /> - - - - {i18n.translate('xpack.graph.bar.exploreLabel', { defaultMessage: 'Explore' })} - - - - -
    + {currentIndexPattern + ? currentIndexPattern.title + : // This branch will be shown if the user exits the + // initial picker modal + i18n.translate('xpack.graph.bar.pickSourceLabel', { + defaultMessage: 'Click here to pick a data source', + })} + + + } + onChange={setQuery} + /> + + + + {i18n.translate('xpack.graph.bar.exploreLabel', { defaultMessage: 'Explore' })} + + + + ); } diff --git a/x-pack/legacy/plugins/graph/public/components/source_modal.tsx b/x-pack/legacy/plugins/graph/public/components/source_modal.tsx index 4c3b3c8be9110..5829370b030e6 100644 --- a/x-pack/legacy/plugins/graph/public/components/source_modal.tsx +++ b/x-pack/legacy/plugins/graph/public/components/source_modal.tsx @@ -12,7 +12,7 @@ import { SourcePicker, SourcePickerProps } from './source_picker'; export function SourceModal(props: SourcePickerProps) { return ( - <> +
    - +
    ); } diff --git a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts b/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts new file mode 100644 index 0000000000000..0a0fc8cae5d26 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getSuitableIcon } from '../helpers/style_choices'; +import { fetchTopNodes } from './fetch_top_nodes'; + +const icon = getSuitableIcon(''); + +describe('fetch_top_nodes', () => { + it('should build terms agg', async () => { + const postMock = jest.fn(() => Promise.resolve({ resp: {} })); + await fetchTopNodes(postMock, 'test', [ + { color: '', hopSize: 5, icon, name: 'field1', selected: false, type: 'string' }, + { color: '', hopSize: 5, icon, name: 'field2', selected: false, type: 'string' }, + ]); + expect(postMock).toHaveBeenCalledWith('../api/graph/searchProxy', { + body: JSON.stringify({ + index: 'test', + body: { + size: 0, + aggs: { + sample: { + sampler: { + shard_size: 5000, + }, + aggs: { + top_values_field1: { + terms: { + field: 'field1', + size: 10, + }, + }, + top_values_field2: { + terms: { + field: 'field2', + size: 10, + }, + }, + }, + }, + }, + }, + }), + }); + }); + + it('should map result to nodes', async () => { + const postMock = jest.fn(() => + Promise.resolve({ + resp: { + aggregations: { + sample: { + top_values_field1: { + buckets: [{ key: 'A' }, { key: 'B' }], + }, + top_values_field2: { + buckets: [{ key: 'C' }, { key: 'D' }], + }, + }, + }, + }, + }) + ); + const result = await fetchTopNodes(postMock, 'test', [ + { color: 'red', hopSize: 5, icon, name: 'field1', selected: false, type: 'string' }, + { color: 'blue', hopSize: 5, icon, name: 'field2', selected: false, type: 'string' }, + ]); + expect(result.length).toEqual(4); + expect(result[0]).toEqual({ + color: 'red', + data: { + field: 'field1', + term: 'A', + }, + field: 'field1', + icon, + id: '', + label: 'A', + term: 'A', + }); + expect(result[2]).toEqual({ + color: 'blue', + data: { + field: 'field2', + term: 'C', + }, + field: 'field2', + icon, + id: '', + label: 'C', + term: 'C', + }); + }); +}); diff --git a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts b/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts new file mode 100644 index 0000000000000..87b33cbe35f82 --- /dev/null +++ b/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreStart } from 'src/core/public'; +import { WorkspaceField, ServerResultNode } from '../types'; + +const DEFAULT_SHARD_SIZE = 5000; + +function createSamplerSearchBody(aggs: object, shardSize: number = DEFAULT_SHARD_SIZE) { + return { + size: 0, + aggs: { + sample: { + sampler: { + shard_size: shardSize, + }, + aggs, + }, + }, + }; +} + +function createTopTermsAggName(fieldName: string) { + return `top_values_${fieldName}`; +} + +function createTopTermsSubAgg(field: string, size: number = 10) { + return { + [createTopTermsAggName(field)]: { + terms: { + field, + size, + }, + }, + }; +} + +// TODO use elasticsearch types here +interface TopTermsAggResponse { + aggregations?: { + sample: Record< + string, + { + buckets: Array<{ key: string; doc_count: number }>; + } + >; + }; +} + +function getTopTermsResult(response: TopTermsAggResponse, fieldName: string) { + if (!response.aggregations) { + return []; + } + return response.aggregations.sample[createTopTermsAggName(fieldName)].buckets.map( + bucket => bucket.key + ); +} + +export function createServerResultNode( + fieldName: string, + term: string, + allFields: WorkspaceField[] +): ServerResultNode { + const field = allFields.find(({ name }) => name === fieldName); + + if (!field) { + throw new Error('Invariant error: field not found'); + } + + return { + field: fieldName, + term, + id: '', + color: field.color, + icon: field.icon, + data: { + field: fieldName, + term, + }, + label: term, + }; +} + +export async function fetchTopNodes( + post: CoreStart['http']['post'], + index: string, + fields: WorkspaceField[] +) { + const aggs = fields + .map(({ name }) => name) + .map(fieldName => createTopTermsSubAgg(fieldName)) + .reduce((allAggs, subAgg) => ({ ...allAggs, ...subAgg })); + const body = createSamplerSearchBody(aggs); + + const response: TopTermsAggResponse = (await post('../api/graph/searchProxy', { + body: JSON.stringify({ index, body }), + })).resp; + + const nodes: ServerResultNode[] = []; + + fields.forEach(({ name }) => { + const topTerms = getTopTermsResult(response, name); + const fieldNodes = topTerms.map(term => createServerResultNode(name, term, fields)); + + nodes.push(...fieldNodes); + }); + + return nodes; +} diff --git a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts b/x-pack/legacy/plugins/graph/public/types/workspace_state.ts index 54666c48161e6..fab093535cb63 100644 --- a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts +++ b/x-pack/legacy/plugins/graph/public/types/workspace_state.ts @@ -41,27 +41,31 @@ export interface WorkspaceEdge { isSelected?: boolean; } -export interface GraphData { - nodes: Array<{ +export interface ServerResultNode { + field: string; + term: string; + id: string; + label: string; + color: string; + icon: FontawesomeIcon; + data: { field: string; term: string; - id: string; - label: string; - color: string; - icon: FontawesomeIcon; - data: { - field: string; - term: string; - }; - }>; - edges: Array<{ - source: number; - target: number; - weight: number; - width: number; - doc_count?: number; - inferred: boolean; - }>; + }; +} + +export interface ServerResultEdge { + source: number; + target: number; + weight: number; + width: number; + doc_count?: number; + inferred: boolean; +} + +export interface GraphData { + nodes: ServerResultNode[]; + edges: ServerResultEdge[]; } export interface Workspace { From ea18949ce72713b8406df72f492a5fe14081817c Mon Sep 17 00:00:00 2001 From: Michail Yasonik Date: Thu, 3 Oct 2019 15:00:12 +0530 Subject: [PATCH 06/35] Variety of quick a11y fixes (#46569) Focusing on heading structure and page layout for Home, Discover, Dashboard, and Visualize. This is progress on #37539 --- src/core/public/chrome/ui/header/header.tsx | 14 +-- .../query_bar_input.test.tsx.snap | 33 ++----- .../query_bar/components/query_bar_input.tsx | 43 ++++---- .../components/options/gauge/labels_panel.tsx | 4 +- .../components/options/gauge/ranges_panel.tsx | 4 +- .../components/options/gauge/style_panel.tsx | 4 +- .../category_axis_panel.test.tsx.snap | 4 +- .../value_axes_panel.test.tsx.snap | 4 +- .../metrics_axes/category_axis_panel.tsx | 4 +- .../options/metrics_axes/series_panel.tsx | 4 +- .../options/metrics_axes/value_axes_panel.tsx | 4 +- .../public/components/options/pie.tsx | 8 +- .../options/point_series/grid_panel.tsx | 4 +- .../options/point_series/point_series.tsx | 4 +- .../options/point_series/threshold_panel.tsx | 4 +- .../public/dashboard/dashboard_app.html | 1 + .../field_chooser/field_chooser.html | 2 +- .../__snapshots__/no_results.test.js.snap | 4 +- .../public/discover/directives/no_results.js | 4 +- .../kibana/public/discover/index.html | 10 +- .../public/visualize/editor/editor.html | 10 ++ .../public/markdown_options.tsx | 4 +- .../public/chrome/directives/kbn_chrome.html | 4 +- .../__snapshots__/agg_group.test.tsx.snap | 4 +- .../editors/default/components/agg_group.tsx | 2 +- .../public/vis/editors/default/sidebar.html | 4 +- .../lib/containers/embeddable_child_panel.tsx | 2 +- .../public/lib/panel/embeddable_panel.tsx | 14 ++- .../lib/panel/panel_header/panel_header.tsx | 97 +++++++++++-------- .../panel/panel_header/panel_options_menu.tsx | 22 ++++- .../public/context_menu/open_context_menu.tsx | 1 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 33 files changed, 186 insertions(+), 145 deletions(-) diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index afd9f8e4a3820..f24b0ed1681aa 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -19,7 +19,7 @@ import Url from 'url'; -import React, { Component, createRef, Fragment } from 'react'; +import React, { Component, createRef } from 'react'; import * as Rx from 'rxjs'; import { @@ -376,7 +376,7 @@ class HeaderUI extends Component { ]; return ( - +
    @@ -407,11 +407,13 @@ class HeaderUI extends Component { isLocked={isLocked} onIsLockedUpdate={onIsLockedUpdate} > - - - + - +
    ); } diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap index da756275a83e9..6fdbf4fce4553 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap @@ -583,10 +583,9 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto onOutsideClick={[Function]} >
    } - aria-activedescendant="" aria-autocomplete="list" - aria-controls="kbnTypeahead__items" - aria-label="You are on search box of Another Screen page. Start typing to search and filter the test" + aria-label="Start typing to search and filter the test page" autoComplete="off" autoFocus={false} compressed={false} @@ -651,10 +648,8 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto >
    } - aria-activedescendant="" aria-autocomplete="list" - aria-controls="kbnTypeahead__items" - aria-label="You are on search box of Another Screen page. Start typing to search and filter the test" + aria-label="Start typing to search and filter the test page" autoComplete="off" autoFocus={true} compressed={false} @@ -1425,10 +1417,8 @@ exports[`QueryBarInput Should pass the query language to the language switcher 1 >
    } - aria-activedescendant="" aria-autocomplete="list" - aria-controls="kbnTypeahead__items" - aria-label="You are on search box of Another Screen page. Start typing to search and filter the test" + aria-label="Start typing to search and filter the test page" autoComplete="off" autoFocus={true} compressed={false} @@ -2199,10 +2186,8 @@ exports[`QueryBarInput Should render the given query 1`] = ` > { } public render() { + const isSuggestionsVisible = this.state.isSuggestionsVisible && { + 'aria-controls': 'kbnTypeahead__items', + 'aria-owns': 'kbnTypeahead__items', + }; + const ariaCombobox = { ...isSuggestionsVisible, role: 'combobox' }; + return (
    { }} autoComplete="off" spellCheck={false} - aria-label={ - this.props.screenTitle - ? this.props.intl.formatMessage( - { - id: 'data.query.queryBar.searchInputAriaLabel', - defaultMessage: - 'You are on search box of {previouslyTranslatedPageTitle} page. Start typing to search and filter the {pageType}', - }, - { - previouslyTranslatedPageTitle: this.props.screenTitle, - pageType: this.services.appName, - } - ) - : undefined - } + aria-label={i18n.translate('data.query.queryBar.searchInputAriaLabel', { + defaultMessage: 'Start typing to search and filter the {pageType} page', + values: { pageType: this.services.appName }, + })} type="text" aria-autocomplete="list" - aria-controls="kbnTypeahead__items" + aria-controls={this.state.isSuggestionsVisible ? 'kbnTypeahead__items' : undefined} aria-activedescendant={ - this.state.isSuggestionsVisible ? 'suggestion-' + this.state.index : '' + this.state.isSuggestionsVisible && typeof this.state.index === 'number' + ? `suggestion-${this.state.index}` + : undefined } role="textbox" prepend={this.props.prepend} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/labels_panel.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/labels_panel.tsx index a24a37ca971d5..b96132fa29380 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/labels_panel.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/labels_panel.tsx @@ -29,12 +29,12 @@ function LabelsPanel({ stateParams, setValue, setGaugeValue }: GaugeOptionsInter return ( -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/ranges_panel.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/ranges_panel.tsx index 4abfb2e604b1d..4e3b511782c9e 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/ranges_panel.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/ranges_panel.tsx @@ -38,12 +38,12 @@ function RangesPanel({ return ( -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/style_panel.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/style_panel.tsx index f606080afbdb2..a76171673d9a8 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/style_panel.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/style_panel.tsx @@ -34,12 +34,12 @@ function StylePanel({ aggs, setGaugeValue, stateParams, vis }: GaugeOptionsInter return ( -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap index 6eef5047634f4..d88654cfdc0c4 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap @@ -7,13 +7,13 @@ exports[`CategoryAxisPanel component should init with the default set of props 1 -

    +

    -

    +
    -

    +

    -

    + -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/series_panel.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/series_panel.tsx index 434202d64d6c3..5a455f4adde31 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/series_panel.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/series_panel.tsx @@ -38,12 +38,12 @@ function SeriesPanel(props: SeriesPanelProps) { return ( -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.tsx index 34a0d2cd981c5..eb0ab4333af59 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.tsx @@ -109,12 +109,12 @@ function ValueAxesPanel(props: ValueAxesPanelProps) { -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/pie.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/pie.tsx index 982c7265d5494..53dde185ec09f 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/pie.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/pie.tsx @@ -36,12 +36,12 @@ function PieOptions(props: VisOptionsProps) { <> -

    +

    -

    +
    ) { -
    +

    -

    +
    -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx index 11034f7f7335e..8e3f66d12b9bd 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx @@ -34,12 +34,12 @@ function PointSeriesOptions(props: VisOptionsProps) { <> -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx index 9877b84345a1f..49e56e377a8d5 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx @@ -42,12 +42,12 @@ function ThresholdPanel({ stateParams, setValue, vis }: VisOptionsProps -

    +

    -

    +
    diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html index 5ceb28e6b225b..39db357a69321 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -120,6 +120,7 @@
    +

    {{screenTitle}}

    diff --git a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html index 2043dc44c147e..d1a75adac5b82 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html +++ b/src/legacy/core_plugins/kibana/public/discover/components/field_chooser/field_chooser.html @@ -84,7 +84,7 @@ i18n-default-message="Selected fields" >
    -
      +
        -

        Expand your time range -

        +

        One or more of the indices you’re looking at contains a date field. Your query may not match anything in the current time range, or there may not be any data at all in the currently selected time range. You can try changing the time range to one which contains data.

        diff --git a/src/legacy/core_plugins/kibana/public/discover/directives/no_results.js b/src/legacy/core_plugins/kibana/public/discover/directives/no_results.js index 9f57c49977f5a..5f6d32681b50e 100644 --- a/src/legacy/core_plugins/kibana/public/discover/directives/no_results.js +++ b/src/legacy/core_plugins/kibana/public/discover/directives/no_results.js @@ -119,12 +119,12 @@ export class DiscoverNoResults extends Component { -

        +

        -

        +

        - +

        {{screenTitle}}

        + +

        +

        +

        -

        Markdown

        +

        + +

        diff --git a/src/legacy/ui/public/chrome/directives/kbn_chrome.html b/src/legacy/ui/public/chrome/directives/kbn_chrome.html index 541082e68de58..ced89287d310f 100644 --- a/src/legacy/ui/public/chrome/directives/kbn_chrome.html +++ b/src/legacy/ui/public/chrome/directives/kbn_chrome.html @@ -1,9 +1,9 @@
        -
        + >
        diff --git a/src/legacy/ui/public/vis/editors/default/components/__snapshots__/agg_group.test.tsx.snap b/src/legacy/ui/public/vis/editors/default/components/__snapshots__/agg_group.test.tsx.snap index 813b7978d2667..29af0887db2b8 100644 --- a/src/legacy/ui/public/vis/editors/default/components/__snapshots__/agg_group.test.tsx.snap +++ b/src/legacy/ui/public/vis/editors/default/components/__snapshots__/agg_group.test.tsx.snap @@ -10,9 +10,9 @@ exports[`DefaultEditorAgg component should init with the default set of props 1` -
        +

        Metrics -

        +
        -
        {groupNameLabel}
        +

        {groupNameLabel}

        diff --git a/src/legacy/ui/public/vis/editors/default/sidebar.html b/src/legacy/ui/public/vis/editors/default/sidebar.html index 0434534bddbfd..b0a03e461fc1c 100644 --- a/src/legacy/ui/public/vis/editors/default/sidebar.html +++ b/src/legacy/ui/public/vis/editors/default/sidebar.html @@ -7,7 +7,7 @@ ng-keydown="submitEditorWithKeyboard($event)" > -
        {{ vis.indexPattern.title }} -
        +