From f786f203166d0df90809da9df71f775907813ecd Mon Sep 17 00:00:00 2001 From: rockfield Date: Tue, 5 Mar 2019 11:47:03 +0300 Subject: [PATCH 01/27] [ML] (Accessibility) "Analyzing Data" should announce --- .../components/about_panel/about_panel.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/about_panel.js b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/about_panel.js index 24ab0c48cd44a..c9a907fe63018 100644 --- a/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/about_panel.js +++ b/x-pack/plugins/ml/public/file_datavisualizer/components/about_panel/about_panel.js @@ -67,6 +67,12 @@ export function LoadingPanel() {

+

+ +

Date: Wed, 6 Mar 2019 18:10:46 +0300 Subject: [PATCH 02/27] add aria label for current auto (not set) color --- .../arg_types/series_style/simple_template.js | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js index a8fa38a3d07e8..a41b514652585 100644 --- a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js +++ b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js @@ -9,11 +9,12 @@ import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiButtonIcon } from '@elastic/eui'; import { set, del } from 'object-path-immutable'; import { get } from 'lodash'; +import { injectI18n } from '@kbn/i18n/react'; import { ColorPickerMini } from '../../../components/color_picker_mini'; import { TooltipIcon } from '../../../components/tooltip_icon'; -export const SimpleTemplate = props => { - const { typeInstance, argValue, onValueChange, labels, workpad } = props; +const SimpleTemplateUI = props => { + const { typeInstance, argValue, onValueChange, labels, workpad, intl } = props; const { name } = typeInstance; const chain = get(argValue, 'chain.0', {}); const chainArgs = get(chain, 'arguments', {}); @@ -36,7 +37,13 @@ export const SimpleTemplate = props => { Color  - handlePlain('color', '#000000')}> + handlePlain('color', '#000000')} + > Auto @@ -78,9 +85,9 @@ export const SimpleTemplate = props => { ); }; -SimpleTemplate.displayName = 'SeriesStyleArgSimpleInput'; +SimpleTemplateUI.displayName = 'SeriesStyleArgSimpleInput'; -SimpleTemplate.propTypes = { +SimpleTemplateUI.propTypes = { onValueChange: PropTypes.func.isRequired, argValue: PropTypes.any.isRequired, labels: PropTypes.array, @@ -88,4 +95,7 @@ SimpleTemplate.propTypes = { colors: PropTypes.array.isRequired, }).isRequired, typeInstance: PropTypes.shape({ name: PropTypes.string.isRequired }).isRequired, + intl: PropTypes.any, }; + +export const SimpleTemplate = injectI18n(SimpleTemplateUI); From 30795ada887999e6905984927f04f379bf56daa7 Mon Sep 17 00:00:00 2001 From: rockfield Date: Thu, 7 Mar 2019 10:23:35 +0300 Subject: [PATCH 03/27] attempts to get intl working --- .../expression_types/arg_types/series_style/simple_template.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js index a41b514652585..4602a8b1b0519 100644 --- a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js +++ b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js @@ -95,7 +95,7 @@ SimpleTemplateUI.propTypes = { colors: PropTypes.array.isRequired, }).isRequired, typeInstance: PropTypes.shape({ name: PropTypes.string.isRequired }).isRequired, - intl: PropTypes.any, + intl: PropTypes.any.isRequired, }; export const SimpleTemplate = injectI18n(SimpleTemplateUI); From 3f030d8e8b9651bde61843921214dcb08c9188ad Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Wed, 6 Mar 2019 13:03:30 +0100 Subject: [PATCH 04/27] fixing pipeline_data_loader (#32478) --- .../public/renderers/visualization.js | 2 +- .../__snapshots__/build_pipeline.test.js.snap | 12 ++++---- .../loader/pipeline_helpers/build_pipeline.ts | 30 +++++++++++++++---- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/legacy/core_plugins/interpreter/public/renderers/visualization.js b/src/legacy/core_plugins/interpreter/public/renderers/visualization.js index df789ae0affd5..a04431c8c4467 100644 --- a/src/legacy/core_plugins/interpreter/public/renderers/visualization.js +++ b/src/legacy/core_plugins/interpreter/public/renderers/visualization.js @@ -49,7 +49,7 @@ export const visualization = () => ({ handlers.onDestroy(() => visualizationLoader.destroy()); - await visualizationLoader.render(domNode, handlers.vis, visData, uiState, params).then(() => { + await visualizationLoader.render(domNode, handlers.vis, visData, visConfig || handlers.vis.params, uiState, params).then(() => { if (handlers.done) handlers.done(); }); }, diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/__snapshots__/build_pipeline.test.js.snap b/src/legacy/ui/public/visualize/loader/pipeline_helpers/__snapshots__/build_pipeline.test.js.snap index 3b919e4eb483a..ae592282316ef 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/__snapshots__/build_pipeline.test.js.snap +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/__snapshots__/build_pipeline.test.js.snap @@ -4,9 +4,9 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles markdown function 1`] = `"kibana_markdown expression='## hello _markdown_' visConfig='{\\"markdown\\":\\"## hello _markdown_\\",\\"foo\\":\\"bar\\"}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function with buckets 1`] = `"kibana_metric visConfig='{\\"dimensions\\":{\\"metrics\\":[0,1],\\"bucket\\":2}}' "`; +exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function with buckets 1`] = `"kibana_metric visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metrics\\":[0,1],\\"bucket\\":2}}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function without buckets 1`] = `"kibana_metric visConfig='{\\"dimensions\\":{\\"metrics\\":[0,1]}}' "`; +exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metric function without buckets 1`] = `"kibana_metric visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metrics\\":[0,1]}}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles metrics/tsvb function 1`] = `"tsvb params='{\\"foo\\":\\"bar\\"}' uiState='{}' "`; @@ -16,17 +16,17 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles region_map function without buckets 1`] = `"regionmap visConfig='{\\"metric\\":0}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits 1`] = `"kibana_table visConfig='{\\"dimensions\\":{\\"metrics\\":[0],\\"buckets\\":[],\\"splitRow\\":[1,2]}}' "`; +exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits 1`] = `"kibana_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[0],\\"buckets\\":[],\\"splitRow\\":[1,2]}}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits and buckets 1`] = `"kibana_table visConfig='{\\"dimensions\\":{\\"metrics\\":[0,1],\\"buckets\\":[3],\\"splitRow\\":[2,4]}}' "`; +exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits and buckets 1`] = `"kibana_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[0,1],\\"buckets\\":[3],\\"splitRow\\":[2,4]}}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function without splits or buckets 1`] = `"kibana_table visConfig='{\\"dimensions\\":{\\"metrics\\":[0,1],\\"buckets\\":[]}}' "`; +exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function without splits or buckets 1`] = `"kibana_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[0,1],\\"buckets\\":[]}}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tagcloud function with buckets 1`] = `"tagcloud visConfig='{\\"metric\\":0,\\"bucket\\":1}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tagcloud function without buckets 1`] = `"tagcloud visConfig='{\\"metric\\":0}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"dimensions\\":{\\"metric\\":0,\\"geohash\\":1,\\"geocentroid\\":3}}' "`; +exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metric\\":0,\\"geohash\\":1,\\"geocentroid\\":3}}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles timelion function 1`] = `"timelion_vis expression='foo' interval='bar' "`; diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.ts index 05ab628a3c689..a855f2489577a 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/build_pipeline.ts @@ -224,27 +224,45 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = { return `kibana_markdown ${expression}${visConfig}`; }, table: (visState, schemas) => { - const visConfig = buildVisConfig.table(schemas); + const visConfig = { + ...visState.params, + ...buildVisConfig.table(schemas), + }; return `kibana_table ${prepareJson('visConfig', visConfig)}`; }, metric: (visState, schemas) => { - const visConfig = buildVisConfig.metric(schemas); + const visConfig = { + ...visState.params, + ...buildVisConfig.metric(schemas), + }; return `kibana_metric ${prepareJson('visConfig', visConfig)}`; }, tagcloud: (visState, schemas) => { - const visConfig = buildVisConfig.tagcloud(schemas); + const visConfig = { + ...visState.params, + ...buildVisConfig.tagcloud(schemas), + }; return `tagcloud ${prepareJson('visConfig', visConfig)}`; }, region_map: (visState, schemas) => { - const visConfig = buildVisConfig.region_map(schemas); + const visConfig = { + ...visState.params, + ...buildVisConfig.region_map(schemas), + }; return `regionmap ${prepareJson('visConfig', visConfig)}`; }, tile_map: (visState, schemas) => { - const visConfig = buildVisConfig.tile_map(schemas); + const visConfig = { + ...visState.params, + ...buildVisConfig.tile_map(schemas), + }; return `tilemap ${prepareJson('visConfig', visConfig)}`; }, pie: (visState, schemas) => { - const visConfig = buildVisConfig.pie(schemas); + const visConfig = { + ...visState.params, + ...buildVisConfig.pie(schemas), + }; return `kibana_pie ${prepareJson('visConfig', visConfig)}`; }, }; From 5461894bd6b6240694ff08cb8d31fbb456969b13 Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Wed, 6 Mar 2019 14:34:20 +0100 Subject: [PATCH 05/27] Fix some strings after translator feedback (#32501) * Fix some strings after translator feedback * Fix jest tests * Fix typo * Fix Mocha tests * Fix more mocha tests --- .../__snapshots__/options_tab.test.js.snap | 2 +- .../public/components/editor/options_tab.js | 2 +- src/legacy/core_plugins/kibana/index.js | 18 ------------------ .../public/dashboard/panel/dashboard_panel.tsx | 2 +- .../public/region_map_vis_params.html | 1 + src/legacy/core_plugins/timelion/index.js | 3 --- .../ui/public/agg_types/buckets/terms.js | 8 +++++--- .../public/agg_types/controls/date_ranges.html | 2 ++ .../disabled_call_out.test.js.snap | 1 + .../scripting_call_outs/disabled_call_out.js | 8 +++++++- .../ui/public/field_editor/field_editor.js | 9 ++++++++- src/legacy/ui/ui_apps/__tests__/ui_app.js | 3 --- src/legacy/ui/ui_apps/ui_app.js | 4 ---- .../ui/ui_nav_links/__tests__/ui_nav_link.js | 8 -------- src/legacy/ui/ui_nav_links/ui_nav_link.js | 3 --- x-pack/plugins/graph/index.js | 1 - .../translations/translations/zh-CN.json | 12 +----------- 17 files changed, 28 insertions(+), 59 deletions(-) diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/options_tab.test.js.snap b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/options_tab.test.js.snap index 522ea00ffd194..0a33e6e171549 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/options_tab.test.js.snap +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/options_tab.test.js.snap @@ -50,7 +50,7 @@ exports[`renders OptionsTab 1`] = ` data-test-subj="inputControlEditorPinFiltersCheckbox" label={ diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.js index 46b0eae158d25..d63ef66117854 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.js +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.js @@ -86,7 +86,7 @@ export class OptionsTab extends Component { } checked={this.props.editorState.params.pinFilters} onChange={this.handlePinFilters} diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 853cf35c8cf86..a6c7262368ff4 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -69,9 +69,6 @@ export default function (kibana) { id: 'kibana', title: 'Kibana', listed: false, - description: i18n.translate('kbn.kibanaDescription', { - defaultMessage: 'the kibana you know and love' - }), main: 'plugins/kibana/kibana', }, styleSheetPaths: resolve(__dirname, 'public/index.scss'), @@ -83,9 +80,6 @@ export default function (kibana) { }), order: -1003, url: `${kbnBaseUrl}#/discover`, - description: i18n.translate('kbn.discoverDescription', { - defaultMessage: 'interactively explore your data' - }), icon: 'plugins/kibana/assets/discover.svg', euiIconType: 'discoverApp', }, { @@ -95,9 +89,6 @@ export default function (kibana) { }), order: -1002, url: `${kbnBaseUrl}#/visualize`, - description: i18n.translate('kbn.visualizeDescription', { - defaultMessage: 'design data visualizations' - }), icon: 'plugins/kibana/assets/visualize.svg', euiIconType: 'visualizeApp', }, { @@ -113,9 +104,6 @@ export default function (kibana) { // the url above in order to preserve the original url for BWC. The subUrlBase helps the Chrome api nav // to determine what url to use for the app link. subUrlBase: `${kbnBaseUrl}#/dashboard`, - description: i18n.translate('kbn.dashboardDescription', { - defaultMessage: 'compose visualizations for much win' - }), icon: 'plugins/kibana/assets/dashboard.svg', euiIconType: 'dashboardApp', }, { @@ -125,9 +113,6 @@ export default function (kibana) { }), order: 9001, url: '/app/kibana#/dev_tools', - description: i18n.translate('kbn.devToolsDescription', { - defaultMessage: 'development tools' - }), icon: 'plugins/kibana/assets/wrench.svg', euiIconType: 'devToolsApp', }, { @@ -137,9 +122,6 @@ export default function (kibana) { }), order: 9003, url: `${kbnBaseUrl}#/management`, - description: i18n.translate('kbn.managementDescription', { - defaultMessage: 'define index patterns, change config, and more' - }), icon: 'plugins/kibana/assets/settings.svg', euiIconType: 'managementApp', linkToLastSubUrl: false diff --git a/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.tsx b/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.tsx index 9ac8b3c49780a..ceb37dee0da19 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/panel/dashboard_panel.tsx @@ -71,7 +71,7 @@ class DashboardPanelUi extends React.Component { ? null : props.intl.formatMessage({ id: 'kbn.dashboard.panel.noEmbeddableFactoryErrorMessage', - defaultMessage: 'No factory found for embeddable', + defaultMessage: 'The feature to render this panel is missing.', }), }; diff --git a/src/legacy/core_plugins/region_map/public/region_map_vis_params.html b/src/legacy/core_plugins/region_map/public/region_map_vis_params.html index e24787be1e330..9e8c872e96d45 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_vis_params.html +++ b/src/legacy/core_plugins/region_map/public/region_map_vis_params.html @@ -136,6 +136,7 @@ for="outlineWeight" i18n-id="regionMap.visParams.outlineWeightLabel" i18n-default-message="Outline weight" + i18n-description="Describes the width of a line surrounding a country on a map." >
@@ -13,6 +14,7 @@ id="visEditorDateRangeTo{{agg.id}}" i18n-id="common.ui.aggTypes.dateRanges.toColumnLabel" i18n-default-message="To" + i18n-description="Describes the end of a date range, e.g. From 2018-02-26 *To* 2018-02-28" > diff --git a/src/legacy/ui/public/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.js.snap b/src/legacy/ui/public/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.js.snap index 7aeade16748ee..fa48075fe1d59 100644 --- a/src/legacy/ui/public/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.js.snap @@ -9,6 +9,7 @@ exports[`ScriptingDisabledCallOut should render normally 1`] = ` title={ diff --git a/src/legacy/ui/public/field_editor/components/scripting_call_outs/disabled_call_out.js b/src/legacy/ui/public/field_editor/components/scripting_call_outs/disabled_call_out.js index 4c3a2a68a84b0..b169dd5fe71af 100644 --- a/src/legacy/ui/public/field_editor/components/scripting_call_outs/disabled_call_out.js +++ b/src/legacy/ui/public/field_editor/components/scripting_call_outs/disabled_call_out.js @@ -32,7 +32,13 @@ export const ScriptingDisabledCallOut = ({ return isVisible ? ( } + title={ + + } color="danger" iconType="alert" > diff --git a/src/legacy/ui/public/field_editor/field_editor.js b/src/legacy/ui/public/field_editor/field_editor.js index 69ef86b11eedf..aa9b0d61dba83 100644 --- a/src/legacy/ui/public/field_editor/field_editor.js +++ b/src/legacy/ui/public/field_editor/field_editor.js @@ -387,7 +387,14 @@ export class FieldEditorComponent extends PureComponent { const { intl } = this.props; return ( - + { expect(JSON.parse(JSON.stringify(app))).to.eql({ id: spec.id, title: spec.title, - description: spec.description, icon: spec.icon, main: spec.main, linkToLastSubUrl: spec.linkToLastSubUrl, @@ -149,7 +147,6 @@ describe('ui apps / UiApp', () => { order: 9000, url: '/app/uiapp-test', subUrlBase: '/app/uiapp-test', - description: 'Test of UI App Constructor', icon: 'ui_app_test.svg', linkToLastSubUrl: true, hidden: false, diff --git a/src/legacy/ui/ui_apps/ui_app.js b/src/legacy/ui/ui_apps/ui_app.js index 1174f9da7f91b..da9c88888b420 100644 --- a/src/legacy/ui/ui_apps/ui_app.js +++ b/src/legacy/ui/ui_apps/ui_app.js @@ -27,7 +27,6 @@ export class UiApp { main, title, order = 0, - description, icon, euiIconType, hidden, @@ -44,7 +43,6 @@ export class UiApp { this._main = main; this._title = title; this._order = order; - this._description = description; this._icon = icon; this._euiIconType = euiIconType; this._linkToLastSubUrl = linkToLastSubUrl; @@ -66,7 +64,6 @@ export class UiApp { id: this._id, title: this._title, order: this._order, - description: this._description, icon: this._icon, euiIconType: this._euiIconType, url: this._url, @@ -118,7 +115,6 @@ export class UiApp { return { id: this._id, title: this._title, - description: this._description, icon: this._icon, euiIconType: this._euiIconType, main: this._main, diff --git a/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js b/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js index a7c7c005ec72b..be20ac0bded72 100644 --- a/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js +++ b/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js @@ -29,7 +29,6 @@ describe('UiNavLink', () => { title: 'Discover', order: -1003, url: '/app/kibana#/discover', - description: 'interactively explore your data', icon: 'plugins/kibana/assets/discover.svg', euiIconType: 'discoverApp', hidden: true, @@ -43,7 +42,6 @@ describe('UiNavLink', () => { order: spec.order, url: spec.url, subUrlBase: spec.url, - description: spec.description, icon: spec.icon, euiIconType: spec.euiIconType, hidden: spec.hidden, @@ -60,7 +58,6 @@ describe('UiNavLink', () => { id: 'kibana:discover', title: 'Discover', url: '/app/kibana#/discover', - description: 'interactively explore your data', icon: 'plugins/kibana/assets/discover.svg', }; const link = new UiNavLink(spec); @@ -74,7 +71,6 @@ describe('UiNavLink', () => { title: 'Discover', order: -1003, url: '/app/kibana#/discover', - description: 'interactively explore your data', icon: 'plugins/kibana/assets/discover.svg', linkToLastSubUrl: false }; @@ -89,7 +85,6 @@ describe('UiNavLink', () => { title: 'Discover', order: -1003, url: '/app/kibana#/discover', - description: 'interactively explore your data', icon: 'plugins/kibana/assets/discover.svg', }; const link = new UiNavLink(spec); @@ -103,7 +98,6 @@ describe('UiNavLink', () => { title: 'Discover', order: -1003, url: '/app/kibana#/discover', - description: 'interactively explore your data', icon: 'plugins/kibana/assets/discover.svg', }; const link = new UiNavLink(spec); @@ -117,7 +111,6 @@ describe('UiNavLink', () => { title: 'Discover', order: -1003, url: '/app/kibana#/discover', - description: 'interactively explore your data', icon: 'plugins/kibana/assets/discover.svg', }; const link = new UiNavLink(spec); @@ -131,7 +124,6 @@ describe('UiNavLink', () => { title: 'Discover', order: -1003, url: '/app/kibana#/discover', - description: 'interactively explore your data', icon: 'plugins/kibana/assets/discover.svg', }; const link = new UiNavLink(spec); diff --git a/src/legacy/ui/ui_nav_links/ui_nav_link.js b/src/legacy/ui/ui_nav_links/ui_nav_link.js index 25f7221b7b4be..7537a60adbcf2 100644 --- a/src/legacy/ui/ui_nav_links/ui_nav_link.js +++ b/src/legacy/ui/ui_nav_links/ui_nav_link.js @@ -25,7 +25,6 @@ export class UiNavLink { order = 0, url, subUrlBase, - description, icon, euiIconType, linkToLastSubUrl = true, @@ -39,7 +38,6 @@ export class UiNavLink { this._order = order; this._url = url; this._subUrlBase = subUrlBase || url; - this._description = description; this._icon = icon; this._euiIconType = euiIconType; this._linkToLastSubUrl = linkToLastSubUrl; @@ -59,7 +57,6 @@ export class UiNavLink { order: this._order, url: this._url, subUrlBase: this._subUrlBase, - description: this._description, icon: this._icon, euiIconType: this._euiIconType, linkToLastSubUrl: this._linkToLastSubUrl, diff --git a/x-pack/plugins/graph/index.js b/x-pack/plugins/graph/index.js index 626142eaf212f..aa11b433475c6 100644 --- a/x-pack/plugins/graph/index.js +++ b/x-pack/plugins/graph/index.js @@ -23,7 +23,6 @@ export function graph(kibana) { order: 9000, icon: 'plugins/graph/icon.png', euiIconType: 'graphApp', - description: 'Graph exploration', main: 'plugins/graph/app', }, styleSheetPaths: resolve(__dirname, 'public/index.scss'), diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6b79cdf89c236..c91eeb08ef7eb 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -109,7 +109,6 @@ "common.ui.aggTypes.buckets.significantTermsLabel": "{fieldName} 中排名前 {size} 的罕见词", "common.ui.aggTypes.buckets.significantTermsTitle": "重要词", "common.ui.aggTypes.buckets.terms.missingBucketLabel": "缺失", - "common.ui.aggTypes.buckets.terms.orderAggTitle": "顺序聚合", "common.ui.aggTypes.buckets.terms.orderAscendingTitle": "升序", "common.ui.aggTypes.buckets.terms.orderDescendingTitle": "降序", "common.ui.aggTypes.buckets.terms.otherBucketLabel": "其他", @@ -763,7 +762,6 @@ "inputControl.editor.listControl.parentLabel": "父控件", "inputControl.editor.listControl.sizeDescription": "选项数目", "inputControl.editor.listControl.sizeLabel": "大小", - "inputControl.editor.optionsTab.pinFiltersLabel": "将筛选固定在全局状态", "inputControl.editor.optionsTab.updateFilterLabel": "每次更改时更新 Kibana 筛选", "inputControl.editor.optionsTab.useTimeFilterLabel": "使用时间筛选", "inputControl.editor.rangeControl.decimalPlacesLabel": "小数位数", @@ -1201,7 +1199,6 @@ "kbn.dashboard.panel.editPanel.displayName": "编辑可视化", "kbn.dashboard.panel.inspectorPanel.displayName": "检查", "kbn.dashboard.panel.invalidVersionErrorMessage": "版本 {version} 无效,应为 {semver}", - "kbn.dashboard.panel.noEmbeddableFactoryErrorMessage": "未找到 Embeddable 的工厂", "kbn.dashboard.panel.noFoundEmbeddableFactoryErrorMessage": "未找到面板类型 {panelType} 的 Embeddable 工厂", "kbn.dashboard.panel.optionsMenu.optionsContextMenuTitle": "选项", "kbn.dashboard.panel.optionsMenu.panelOptionsButtonAriaLabel": "面板选项", @@ -1256,11 +1253,9 @@ "kbn.dashboard.topNave.viewConfigDescription": "取消编辑并切换到仅查看模式", "kbn.dashboard.urlWasRemovedInSixZeroWarningMessage": "6.0 中未移除 url“dashboard/create”。请更新您的书签。", "kbn.dashboard.visitVisualizeAppLinkText": "访问 Visualize 应用", - "kbn.dashboardDescription": "创作吸引注意力的可视化", "kbn.dashboardTitle": "仪表板", "kbn.devTools.consoleDescription": "跳过 cURL 并使用此 JSON 接口来直接处理您的数据。", "kbn.devTools.consoleTitle": "Console", - "kbn.devToolsDescription": "开发工具", "kbn.devToolsTitle": "开发工具", "kbn.discover.backToTopLinkText": "返至顶部。", "kbn.discover.bucketIntervalTooltip": "此时间间隔将创建{bucketsDescription}而无法在选定时间范围内显示 ,因此其已缩放至 {bucketIntervalDescription}", @@ -1346,7 +1341,6 @@ "kbn.discover.topNav.openSearchPanel.noSearchesFoundDescription": "未找到匹配的搜索。", "kbn.discover.topNav.openSearchPanel.openSearchTitle": "打开搜索", "kbn.discover.valueIsNotConfiguredIndexPatternIDWarningTitle": "{stateVal} 不是配置的索引模式 ID", - "kbn.discoverDescription": "以交互方式浏览您的数据", "kbn.discoverTitle": "Discover", "kbn.doc.couldNotFindDocumentsDescription": "抱歉,我无法在该索引中找到任何匹配该 ID 且为该类型的文档。我已进行非常努力的尝试。我希望它存在。有时候,我觉得文件一定长了腿,自行逃离了索引。有点诡异。我希望能够提供一些建议让您会感觉好一点", "kbn.doc.failedToExecuteQueryDescription": "无法执行查询", @@ -1437,7 +1431,6 @@ "kbn.home.tutorial.unhandledInstructionTypeErrorDescription": "未处理的指令类型 {visibleInstructions}", "kbn.home.welcomeDescription": "您了解 Elastic Stack 的窗口", "kbn.home.welcomeTitle": "欢迎使用 Kibana", - "kbn.kibanaDescription": "您了解并喜爱的 kibana", "kbn.management.createIndexPattern.betaLabel": "公测版", "kbn.management.createIndexPattern.emptyState.checkDataButton": "检查新数据", "kbn.management.createIndexPattern.emptyStateHeader": "找不到任何 Elasticsearch 数据", @@ -1728,7 +1721,6 @@ "kbn.management.settings.searchBar.unableToParseQueryErrorMessage": "无法解析查询", "kbn.management.settings.searchBarAriaLabel": "搜索高级设置", "kbn.management.settings.sectionLabel": "高级设置", - "kbn.managementDescription": "定义索引模式、更改配置等等", "kbn.managementTitle": "管理", "kbn.server.tutorials.aerospikeMetrics.artifacts.application.label": "Discover", "kbn.server.tutorials.aerospikeMetrics.longDescription": "Metricbeat 模块 `aerospike` 从 Aerospike 提取内部指标。[了解详情]({learnMoreLink})。", @@ -2055,7 +2047,6 @@ "kbn.visualize.topNavMenu.shareVisualizationButtonAriaLabel": "共享可视化", "kbn.visualize.visualizeDescription": "创建可视化并聚合存储在 Elasticsearch 索引中的数据。", "kbn.visualize.visualizeListingBreadcrumbsTitle": "可视化", - "kbn.visualizeDescription": "设计数据可视化", "kbn.visualizeTitle": "可视化", "kbnDocViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip": "不支持以 {underscoreSign} 开头的字段名称", "kbnDocViews.table.filterForFieldPresentButtonAriaLabel": "筛留存在的字段", @@ -2414,7 +2405,6 @@ "tileMap.wmsOptions.wmsStylesLabel": "WMS 样式*", "tileMap.wmsOptions.wmsUrlLabel": "WMS url*", "tileMap.wmsOptions.wmsVersionLabel": "WMS 版本*", - "timelion.appDescription": "适用于所有内容的时序表达式", "timelion.cells.actions.fullscreenAriaLabel": "全屏图表", "timelion.cells.actions.fullscreenTooltip": "全屏", "timelion.cells.actions.removeAriaLabel": "删除图表", @@ -8215,4 +8205,4 @@ "xpack.watcher.watchActionsTitle": "满足后将执行 {watchActionsCount, plural, one{# 个操作} other {# 个操作}}", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} +} \ No newline at end of file From 290a58ebcb7d1a12874d9853b7c644a919cc5c12 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 6 Mar 2019 06:42:18 -0700 Subject: [PATCH 06/27] [Infra UI] Add Legend Settings for Waffle Map (#32228) * Adding legend controls * Adding url support and finishing feature * Moving config to right side; setting min/max to data boundries when deactivating auto * Removing legend from max and min labels * removing uneseccary unshift(0) * Change autobounds behavior --- .../components/nodes_overview/index.tsx | 40 +++-- .../components/waffle/gradient_legend.tsx | 2 +- .../infra/public/components/waffle/legend.tsx | 24 ++- .../components/waffle/legend_controls.tsx | 170 ++++++++++++++++++ .../infra/public/components/waffle/map.tsx | 9 +- .../public/components/waffle/steps_legend.tsx | 2 +- .../containers/waffle/with_waffle_options.tsx | 37 +++- .../infra/public/pages/home/page_content.tsx | 12 +- .../store/local/waffle_options/actions.ts | 4 +- .../store/local/waffle_options/reducer.ts | 19 +- .../store/local/waffle_options/selector.ts | 2 + 11 files changed, 298 insertions(+), 23 deletions(-) create mode 100644 x-pack/plugins/infra/public/components/waffle/legend_controls.tsx diff --git a/x-pack/plugins/infra/public/components/nodes_overview/index.tsx b/x-pack/plugins/infra/public/components/nodes_overview/index.tsx index 0d45148aa4cfb..45099ae94ee19 100644 --- a/x-pack/plugins/infra/public/components/nodes_overview/index.tsx +++ b/x-pack/plugins/infra/public/components/nodes_overview/index.tsx @@ -34,6 +34,8 @@ interface Props { onViewChange: (view: string) => void; view: string; intl: InjectedIntl; + boundsOverride: InfraWaffleMapBounds; + autoBounds: boolean; } interface MetricFormatter { @@ -51,12 +53,10 @@ const METRIC_FORMATTERS: MetricFormatters = { [InfraMetricType.cpu]: { formatter: InfraFormatterType.percent, template: '{{value}}', - bounds: { min: 0, max: 1 }, }, [InfraMetricType.memory]: { formatter: InfraFormatterType.percent, template: '{{value}}', - bounds: { min: 0, max: 1 }, }, [InfraMetricType.rx]: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, [InfraMetricType.tx]: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, @@ -67,19 +67,33 @@ const METRIC_FORMATTERS: MetricFormatters = { }; const calculateBoundsFromNodes = (nodes: InfraNode[]): InfraWaffleMapBounds => { - const values = nodes.map(node => node.metric.value); - // if there is only one value then we need to set the bottom range to zero - if (values.length === 1) { - values.unshift(0); + const maxValues = nodes.map(node => node.metric.max); + const minValues = nodes.map(node => node.metric.value); + // if there is only one value then we need to set the bottom range to zero for min + // otherwise the legend will look silly since both values are the same for top and + // bottom. + if (minValues.length === 1) { + minValues.unshift(0); } - return { min: min(values) || 0, max: max(values) || 0 }; + return { min: min(minValues) || 0, max: max(maxValues) || 0 }; }; export const NodesOverview = injectI18n( class extends React.Component { public static displayName = 'Waffle'; public render() { - const { loading, nodes, nodeType, reload, intl, view, options, timeRange } = this.props; + const { + autoBounds, + boundsOverride, + loading, + nodes, + nodeType, + reload, + intl, + view, + options, + timeRange, + } = this.props; if (loading) { return ( ); } - const { metric } = this.props.options; - const metricFormatter = get( - METRIC_FORMATTERS, - metric.type, - METRIC_FORMATTERS[InfraMetricType.count] - ); - const bounds = (metricFormatter && metricFormatter.bounds) || calculateBoundsFromNodes(nodes); + const dataBounds = calculateBoundsFromNodes(nodes); + const bounds = autoBounds ? dataBounds : boundsOverride; return ( @@ -146,6 +155,7 @@ export const NodesOverview = injectI18n( timeRange={timeRange} onFilter={this.handleDrilldown} bounds={bounds} + dataBounds={dataBounds} /> )} diff --git a/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx b/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx index 24ec7bafd391f..c3ea29cd216b1 100644 --- a/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx +++ b/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx @@ -57,7 +57,7 @@ const GradientLegendContainer = styled.div` height: 10px; bottom: 0; left: 0; - right: 0; + right: 40px; `; const GradientLegendTick = styled.div` diff --git a/x-pack/plugins/infra/public/components/waffle/legend.tsx b/x-pack/plugins/infra/public/components/waffle/legend.tsx index 2ce97372a9726..55f7ed0e87622 100644 --- a/x-pack/plugins/infra/public/components/waffle/legend.tsx +++ b/x-pack/plugins/infra/public/components/waffle/legend.tsx @@ -5,19 +5,41 @@ */ import React from 'react'; import styled from 'styled-components'; +import { WithWaffleOptions } from '../../containers/waffle/with_waffle_options'; import { InfraFormatter, InfraWaffleMapBounds, InfraWaffleMapLegend } from '../../lib/lib'; import { GradientLegend } from './gradient_legend'; +import { LegendControls } from './legend_controls'; import { isInfraWaffleMapGradientLegend, isInfraWaffleMapStepLegend } from './lib/type_guards'; import { StepLegend } from './steps_legend'; interface Props { legend: InfraWaffleMapLegend; bounds: InfraWaffleMapBounds; + dataBounds: InfraWaffleMapBounds; formatter: InfraFormatter; } -export const Legend: React.SFC = ({ legend, bounds, formatter }) => { +interface LegendControlOptions { + auto: boolean; + bounds: InfraWaffleMapBounds; +} + +export const Legend: React.SFC = ({ dataBounds, legend, bounds, formatter }) => { return ( + + {({ changeBoundsOverride, changeAutoBounds, autoBounds, boundsOverride }) => ( + { + changeBoundsOverride(options.bounds); + changeAutoBounds(options.auto); + }} + /> + )} + {isInfraWaffleMapGradientLegend(legend) && ( )} diff --git a/x-pack/plugins/infra/public/components/waffle/legend_controls.tsx b/x-pack/plugins/infra/public/components/waffle/legend_controls.tsx new file mode 100644 index 0000000000000..acd974d2099c2 --- /dev/null +++ b/x-pack/plugins/infra/public/components/waffle/legend_controls.tsx @@ -0,0 +1,170 @@ +/* + * 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 { + EuiButton, + EuiButtonIcon, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiPopover, + EuiPopoverTitle, + EuiSwitch, + EuiText, +} from '@elastic/eui'; +import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import React, { SyntheticEvent, useState } from 'react'; +import styled from 'styled-components'; +import { InfraWaffleMapBounds } from '../../lib/lib'; + +interface Props { + onChange: (options: { auto: boolean; bounds: InfraWaffleMapBounds }) => void; + bounds: InfraWaffleMapBounds; + dataBounds: InfraWaffleMapBounds; + autoBounds: boolean; + boundsOverride: InfraWaffleMapBounds; + intl: InjectedIntl; +} + +export const LegendControls = injectI18n( + ({ intl, autoBounds, boundsOverride, onChange, dataBounds }: Props) => { + const [isPopoverOpen, setPopoverState] = useState(false); + const [draftAuto, setDraftAuto] = useState(autoBounds); + const [draftBounds, setDraftBounds] = useState(autoBounds ? dataBounds : boundsOverride); // should come from bounds prop + const buttonComponent = ( + setPopoverState(true)} + /> + ); + + const handleAutoChange = (e: SyntheticEvent) => { + setDraftAuto(e.currentTarget.checked); + }; + + const createBoundsHandler = (name: string) => (e: SyntheticEvent) => { + const value = parseFloat(e.currentTarget.value); + setDraftBounds({ ...draftBounds, [name]: value }); + }; + + const handlePopoverClose = () => { + setPopoverState(false); + }; + + const handleApplyClick = () => { + onChange({ auto: draftAuto, bounds: draftBounds }); + }; + + const commited = + draftAuto === autoBounds && + boundsOverride.min === draftBounds.min && + boundsOverride.max === draftBounds.max; + + const boundsValidRange = draftBounds.min < draftBounds.max; + + return ( + + + Legend Options + + + + + {(!boundsValidRange && ( + +

+ +

+
+ )) || + null} + + + + + + + + + + + + + + + +
+
+
+ ); + } +); + +const ControlContainer = styled.div` + position: absolute; + top: -20px; + right: 6px; + bottom: 0; +`; diff --git a/x-pack/plugins/infra/public/components/waffle/map.tsx b/x-pack/plugins/infra/public/components/waffle/map.tsx index 0bd4c5a8e388b..435fb63dd5def 100644 --- a/x-pack/plugins/infra/public/components/waffle/map.tsx +++ b/x-pack/plugins/infra/public/components/waffle/map.tsx @@ -26,6 +26,7 @@ interface Props { timeRange: InfraTimerangeInput; onFilter: (filter: string) => void; bounds: InfraWaffleMapBounds; + dataBounds: InfraWaffleMapBounds; } export const Map: React.SFC = ({ @@ -36,6 +37,7 @@ export const Map: React.SFC = ({ formatter, bounds, nodeType, + dataBounds, }) => { const map = nodesToWaffleMap(nodes); return ( @@ -80,7 +82,12 @@ export const Map: React.SFC = ({ } })} - + ); }} diff --git a/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx b/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx index 12964989e1b18..732578da792c7 100644 --- a/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx +++ b/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx @@ -48,7 +48,7 @@ export const StepLegend: React.SFC = ({ legend, formatter }) => { const StepLegendContainer = styled.div` display: flex; - padding: 10px; + padding: 10px 40px 10px 10px; `; const StepContainer = styled.div` diff --git a/x-pack/plugins/infra/public/containers/waffle/with_waffle_options.tsx b/x-pack/plugins/infra/public/containers/waffle/with_waffle_options.tsx index b71cb00ed2dd4..733e3eecc705e 100644 --- a/x-pack/plugins/infra/public/containers/waffle/with_waffle_options.tsx +++ b/x-pack/plugins/infra/public/containers/waffle/with_waffle_options.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; +import { isBoolean, isNumber } from 'lodash'; import { InfraMetricInput, InfraMetricType, @@ -26,12 +27,16 @@ const selectOptionsUrlState = createSelector( waffleOptionsSelectors.selectGroupBy, waffleOptionsSelectors.selectNodeType, waffleOptionsSelectors.selectCustomOptions, - (metric, view, groupBy, nodeType, customOptions) => ({ + waffleOptionsSelectors.selectBoundsOverride, + waffleOptionsSelectors.selectAutoBounds, + (metric, view, groupBy, nodeType, customOptions, boundsOverride, autoBounds) => ({ metric, groupBy, nodeType, view, customOptions, + boundsOverride, + autoBounds, }) ); @@ -42,6 +47,8 @@ export const withWaffleOptions = connect( nodeType: waffleOptionsSelectors.selectNodeType(state), view: waffleOptionsSelectors.selectView(state), customOptions: waffleOptionsSelectors.selectCustomOptions(state), + boundsOverride: waffleOptionsSelectors.selectBoundsOverride(state), + autoBounds: waffleOptionsSelectors.selectAutoBounds(state), urlState: selectOptionsUrlState(state), }), bindPlainActionCreators({ @@ -50,6 +57,8 @@ export const withWaffleOptions = connect( changeNodeType: waffleOptionsActions.changeNodeType, changeView: waffleOptionsActions.changeView, changeCustomOptions: waffleOptionsActions.changeCustomOptions, + changeBoundsOverride: waffleOptionsActions.changeBoundsOverride, + changeAutoBounds: waffleOptionsActions.changeAutoBounds, }) ); @@ -65,6 +74,8 @@ interface WaffleOptionsUrlState { nodeType?: ReturnType; view?: ReturnType; customOptions?: ReturnType; + bounds?: ReturnType; + auto?: ReturnType; } export const WithWaffleOptionsUrlState = () => ( @@ -76,6 +87,8 @@ export const WithWaffleOptionsUrlState = () => ( changeNodeType, changeView, changeCustomOptions, + changeAutoBounds, + changeBoundsOverride, }) => ( ( if (newUrlState && newUrlState.customOptions) { changeCustomOptions(newUrlState.customOptions); } + if (newUrlState && newUrlState.bounds) { + changeBoundsOverride(newUrlState.bounds); + } + if (newUrlState && newUrlState.auto) { + changeAutoBounds(newUrlState.auto); + } }} onInitialize={initialUrlState => { if (initialUrlState && initialUrlState.metric) { @@ -114,6 +133,12 @@ export const WithWaffleOptionsUrlState = () => ( if (initialUrlState && initialUrlState.customOptions) { changeCustomOptions(initialUrlState.customOptions); } + if (initialUrlState && initialUrlState.bounds) { + changeBoundsOverride(initialUrlState.bounds); + } + if (initialUrlState && initialUrlState.auto) { + changeAutoBounds(initialUrlState.auto); + } }} /> )} @@ -128,6 +153,8 @@ const mapToUrlState = (value: any): WaffleOptionsUrlState | undefined => nodeType: mapToNodeTypeUrlState(value.nodeType), view: mapToViewUrlState(value.view), customOptions: mapToCustomOptionsUrlState(value.customOptions), + bounds: mapToBoundsOverideUrlState(value.boundsOverride), + auto: mapToAutoBoundsUrlState(value.autoBounds), } : undefined; @@ -169,3 +196,11 @@ const mapToCustomOptionsUrlState = (subject: any) => { ? subject : undefined; }; + +const mapToBoundsOverideUrlState = (subject: any) => { + return subject != null && isNumber(subject.max) && isNumber(subject.min) ? subject : undefined; +}; + +const mapToAutoBoundsUrlState = (subject: any) => { + return subject != null && isBoolean(subject) ? subject : undefined; +}; diff --git a/x-pack/plugins/infra/public/pages/home/page_content.tsx b/x-pack/plugins/infra/public/pages/home/page_content.tsx index 5446f87a0ce47..26a5c1d68a08d 100644 --- a/x-pack/plugins/infra/public/pages/home/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/home/page_content.tsx @@ -27,7 +27,15 @@ export const HomePageContent: React.SFC = () => ( {({ currentTimeRange, isAutoReloading }) => ( - {({ metric, groupBy, nodeType, view, changeView }) => ( + {({ + metric, + groupBy, + nodeType, + view, + changeView, + autoBounds, + boundsOverride, + }) => ( ( timeRange={currentTimeRange} view={view} onViewChange={changeView} + autoBounds={autoBounds} + boundsOverride={boundsOverride} /> )} diff --git a/x-pack/plugins/infra/public/store/local/waffle_options/actions.ts b/x-pack/plugins/infra/public/store/local/waffle_options/actions.ts index 5ef315e4b36d8..44560d64595e1 100644 --- a/x-pack/plugins/infra/public/store/local/waffle_options/actions.ts +++ b/x-pack/plugins/infra/public/store/local/waffle_options/actions.ts @@ -6,7 +6,7 @@ import actionCreatorFactory from 'typescript-fsa'; import { InfraMetricInput, InfraNodeType, InfraPathInput } from '../../../graphql/types'; -import { InfraGroupByOptions } from '../../../lib/lib'; +import { InfraGroupByOptions, InfraWaffleMapBounds } from '../../../lib/lib'; const actionCreator = actionCreatorFactory('x-pack/infra/local/waffle_options'); @@ -15,3 +15,5 @@ export const changeGroupBy = actionCreator('CHANGE_GROUP_BY'); export const changeCustomOptions = actionCreator('CHANGE_CUSTOM_OPTIONS'); export const changeNodeType = actionCreator('CHANGE_NODE_TYPE'); export const changeView = actionCreator('CHANGE_VIEW'); +export const changeBoundsOverride = actionCreator('CHANGE_BOUNDS_OVERRIDE'); +export const changeAutoBounds = actionCreator('CHANGE_AUTO_BOUNDS'); diff --git a/x-pack/plugins/infra/public/store/local/waffle_options/reducer.ts b/x-pack/plugins/infra/public/store/local/waffle_options/reducer.ts index d778eee60afb7..5633ee969f309 100644 --- a/x-pack/plugins/infra/public/store/local/waffle_options/reducer.ts +++ b/x-pack/plugins/infra/public/store/local/waffle_options/reducer.ts @@ -13,8 +13,10 @@ import { InfraNodeType, InfraPathInput, } from '../../../graphql/types'; -import { InfraGroupByOptions } from '../../../lib/lib'; +import { InfraGroupByOptions, InfraWaffleMapBounds } from '../../../lib/lib'; import { + changeAutoBounds, + changeBoundsOverride, changeCustomOptions, changeGroupBy, changeMetric, @@ -28,6 +30,8 @@ export interface WaffleOptionsState { nodeType: InfraNodeType; view: string; customOptions: InfraGroupByOptions[]; + boundsOverride: InfraWaffleMapBounds; + autoBounds: boolean; } export const initialWaffleOptionsState: WaffleOptionsState = { @@ -36,6 +40,8 @@ export const initialWaffleOptionsState: WaffleOptionsState = { nodeType: InfraNodeType.host, view: 'map', customOptions: [], + boundsOverride: { max: 1, min: 0 }, + autoBounds: true, }; const currentMetricReducer = reducerWithInitialState(initialWaffleOptionsState.metric).case( @@ -62,10 +68,21 @@ const currentViewReducer = reducerWithInitialState(initialWaffleOptionsState.vie (current, target) => target ); +const currentBoundsOverrideReducer = reducerWithInitialState( + initialWaffleOptionsState.boundsOverride +).case(changeBoundsOverride, (current, target) => target); + +const currentAutoBoundsReducer = reducerWithInitialState(initialWaffleOptionsState.autoBounds).case( + changeAutoBounds, + (current, target) => target +); + export const waffleOptionsReducer = combineReducers({ metric: currentMetricReducer, groupBy: currentGroupByReducer, nodeType: currentNodeTypeReducer, view: currentViewReducer, customOptions: currentCustomOptionsReducer, + boundsOverride: currentBoundsOverrideReducer, + autoBounds: currentAutoBoundsReducer, }); diff --git a/x-pack/plugins/infra/public/store/local/waffle_options/selector.ts b/x-pack/plugins/infra/public/store/local/waffle_options/selector.ts index d5ee4c2071539..a556cb277777e 100644 --- a/x-pack/plugins/infra/public/store/local/waffle_options/selector.ts +++ b/x-pack/plugins/infra/public/store/local/waffle_options/selector.ts @@ -11,3 +11,5 @@ export const selectGroupBy = (state: WaffleOptionsState) => state.groupBy; export const selectCustomOptions = (state: WaffleOptionsState) => state.customOptions; export const selectNodeType = (state: WaffleOptionsState) => state.nodeType; export const selectView = (state: WaffleOptionsState) => state.view; +export const selectBoundsOverride = (state: WaffleOptionsState) => state.boundsOverride; +export const selectAutoBounds = (state: WaffleOptionsState) => state.autoBounds; From d2bc6b0eff5cbe6f9f2f9b938bc20cb08200e056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Wed, 6 Mar 2019 14:34:00 +0000 Subject: [PATCH 07/27] [CCR] Fix form bug + add missing i18n (#32544) * [Rollup] Fix month value in form select * [Rollup] Add missing i18n for field types --- .../crud_app/sections/job_create/job_create.js | 15 ++++++++++++--- .../public/crud_app/services/humanized_numbers.js | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_create/job_create.js b/x-pack/plugins/rollup/public/crud_app/sections/job_create/job_create.js index a1c5fbeb97200..9788b2fdfc36a 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_create/job_create.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_create/job_create.js @@ -192,9 +192,18 @@ export class JobCreateUi extends Component { )]; } - const formattedNumericFields = formatFields(numericFields, 'numeric'); - const formattedKeywordFields = formatFields(keywordFields, 'keyword'); - const formattedDateFields = formatFields(indexPatternDateFields, 'date'); + const formattedNumericFields = formatFields( + numericFields, + i18n.translate('xpack.rollupJobs.create.numericTypeField', { defaultMessage: 'numeric' }) + ); + const formattedKeywordFields = formatFields( + keywordFields, + i18n.translate('xpack.rollupJobs.create.keywordTypeField', { defaultMessage: 'keyword' }) + ); + const formattedDateFields = formatFields( + indexPatternDateFields, + i18n.translate('xpack.rollupJobs.create.dateTypeField', { defaultMessage: 'date' }) + ); function sortFields(a, b) { const nameA = a.name.toUpperCase(); diff --git a/x-pack/plugins/rollup/public/crud_app/services/humanized_numbers.js b/x-pack/plugins/rollup/public/crud_app/services/humanized_numbers.js index a3c108a4c1ba2..ce779e62df926 100644 --- a/x-pack/plugins/rollup/public/crud_app/services/humanized_numbers.js +++ b/x-pack/plugins/rollup/public/crud_app/services/humanized_numbers.js @@ -21,8 +21,8 @@ const dayOrdinalToDayNameMap = { const monthOrdinalToMonthNameMap = { 0: i18n.translate('xpack.rollupJobs.util.month.january', { defaultMessage: 'January' }), 1: i18n.translate('xpack.rollupJobs.util.month.february', { defaultMessage: 'February' }), - 2: i18n.translate('xpack.rollupJobs.util.month.march', { defaultMessage: 'April' }), - 3: i18n.translate('xpack.rollupJobs.util.month.april', { defaultMessage: 'March' }), + 2: i18n.translate('xpack.rollupJobs.util.month.march', { defaultMessage: 'March' }), + 3: i18n.translate('xpack.rollupJobs.util.month.april', { defaultMessage: 'April' }), 4: i18n.translate('xpack.rollupJobs.util.month.may', { defaultMessage: 'May' }), 5: i18n.translate('xpack.rollupJobs.util.month.june', { defaultMessage: 'June' }), 6: i18n.translate('xpack.rollupJobs.util.month.july', { defaultMessage: 'July' }), From 3f24ccc9af55e59f2a6e7fa54b56578cb61c44f5 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 6 Mar 2019 07:25:59 -0800 Subject: [PATCH 08/27] Typescript-ify FTR Remote (#32447) * [ftr] flatten GenericProviderTypes to fix "log" types * [ftr/remote] typscript-ify * remove webdriver types, since they're version 3 and we're using version 4 * simplify initWebDriver() function * keep jest tests in the functional tests as js, mixing jest and mocha types doesn't work --- package.json | 1 + packages/kbn-test/types/ftr.d.ts | 17 +++-- src/functional_test_runner/types.ts | 27 ------- .../services/remote/{index.js => index.ts} | 0 ...lel_calls.js => prevent_parallel_calls.ts} | 52 +++++++------ .../services/remote/{remote.js => remote.ts} | 33 ++++++--- .../services/remote/verbose_remote_logging.js | 56 -------------- .../remote/{webdriver.js => webdriver.ts} | 74 +++++++++++-------- yarn.lock | 50 ++++++------- 9 files changed, 131 insertions(+), 179 deletions(-) delete mode 100644 src/functional_test_runner/types.ts rename test/functional/services/remote/{index.js => index.ts} (100%) rename test/functional/services/remote/{prevent_parallel_calls.js => prevent_parallel_calls.ts} (54%) rename test/functional/services/remote/{remote.js => remote.ts} (68%) delete mode 100644 test/functional/services/remote/verbose_remote_logging.js rename test/functional/services/remote/{webdriver.js => webdriver.ts} (62%) diff --git a/package.json b/package.json index 2b03852980a2f..0a19f7e20cffd 100644 --- a/package.json +++ b/package.json @@ -267,6 +267,7 @@ "@types/bluebird": "^3.1.1", "@types/boom": "^7.2.0", "@types/chance": "^1.0.0", + "@types/chromedriver": "^2.38.0", "@types/classnames": "^2.2.3", "@types/d3": "^3.5.41", "@types/dedent": "^0.7.0", diff --git a/packages/kbn-test/types/ftr.d.ts b/packages/kbn-test/types/ftr.d.ts index 8cc4253120635..4d632047fa663 100644 --- a/packages/kbn-test/types/ftr.d.ts +++ b/packages/kbn-test/types/ftr.d.ts @@ -17,7 +17,8 @@ * under the License. */ -import { DefaultServiceProviders } from '../../../src/functional_test_runner/types'; +import { ToolingLog } from '@kbn/dev-utils'; +import { Config, Lifecycle } from '../../../src/functional_test_runner/lib'; interface AsyncInstance { /** @@ -39,22 +40,23 @@ type MaybeAsyncInstance = T extends Promise ? AsyncInstance & X : * Convert a map of providers to a map of the instance types they provide, also converting * promise types into the async instances that other providers will receive. */ -type ProvidedTypeMap = { +type ProvidedTypeMap = { [K in keyof T]: T[K] extends (...args: any[]) => any ? MaybeAsyncInstance> - : never + : unknown }; export interface GenericFtrProviderContext< - ServiceProviders extends object, - PageObjectProviders extends object, - ServiceMap = ProvidedTypeMap, + ServiceProviders extends {}, + PageObjectProviders extends {}, + ServiceMap = ProvidedTypeMap, PageObjectMap = ProvidedTypeMap > { /** * Determine if a service is avaliable * @param serviceName */ + hasService(serviceName: 'config' | 'log' | 'lifecycle'): true; hasService(serviceName: K): serviceName is K; hasService(serviceName: string): serviceName is keyof ServiceMap; @@ -63,6 +65,9 @@ export interface GenericFtrProviderContext< * outside of a test/hook, then make sure to call its `.init()` method and await it's promise. * @param serviceName */ + getService(serviceName: 'config'): Config; + getService(serviceName: 'log'): ToolingLog; + getService(serviceName: 'lifecycle'): Lifecycle; getService(serviceName: T): ServiceMap[T]; /** diff --git a/src/functional_test_runner/types.ts b/src/functional_test_runner/types.ts deleted file mode 100644 index 0d53838b1e16c..0000000000000 --- a/src/functional_test_runner/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ToolingLog } from '@kbn/dev-utils'; -import { Config, Lifecycle } from './lib'; - -export interface DefaultServiceProviders { - config(): Config; - log(): ToolingLog; - lifecycle(): Lifecycle; -} diff --git a/test/functional/services/remote/index.js b/test/functional/services/remote/index.ts similarity index 100% rename from test/functional/services/remote/index.js rename to test/functional/services/remote/index.ts diff --git a/test/functional/services/remote/prevent_parallel_calls.js b/test/functional/services/remote/prevent_parallel_calls.ts similarity index 54% rename from test/functional/services/remote/prevent_parallel_calls.js rename to test/functional/services/remote/prevent_parallel_calls.ts index 511c205093ea3..b2cedcbd5da98 100644 --- a/test/functional/services/remote/prevent_parallel_calls.js +++ b/test/functional/services/remote/prevent_parallel_calls.ts @@ -17,34 +17,44 @@ * under the License. */ -export function preventParallelCalls(fn, filter) { - const execQueue = []; +export function preventParallelCalls( + fn: (this: C, arg: A) => Promise, + filter: (arg: A) => boolean +) { + const execQueue: Task[] = []; - return async function (arg) { - if (filter(arg)) { - return await fn.call(this, arg); + class Task { + public promise: Promise; + private resolve!: (result: R) => void; + private reject!: (error: Error) => void; + + constructor(private readonly context: C, private readonly arg: A) { + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }); } - const task = { - exec: async () => { - try { - task.resolve(await fn.call(this, arg)); - } catch (error) { - task.reject(error); - } finally { - execQueue.shift(); - if (execQueue.length) { - execQueue[0].exec(); - } + public async exec() { + try { + this.resolve(await fn.call(this.context, this.arg)); + } catch (error) { + this.reject(error); + } finally { + execQueue.shift(); + if (execQueue.length) { + execQueue[0].exec(); } } - }; + } + } - task.promise = new Promise((resolve, reject) => { - task.resolve = resolve; - task.reject = reject; - }); + return async function(this: C, arg: A) { + if (filter(arg)) { + return await fn.call(this, arg); + } + const task = new Task(this, arg); if (execQueue.push(task) === 1) { // only item in the queue, kick it off task.exec(); diff --git a/test/functional/services/remote/remote.js b/test/functional/services/remote/remote.ts similarity index 68% rename from test/functional/services/remote/remote.js rename to test/functional/services/remote/remote.ts index cea5f2b69d3a9..c4cf50b02046b 100644 --- a/test/functional/services/remote/remote.js +++ b/test/functional/services/remote/remote.ts @@ -17,32 +17,42 @@ * under the License. */ +import { FtrProviderContext } from '../../ftr_provider_context'; import { initWebDriver } from './webdriver'; -export async function RemoteProvider({ getService }) { +export async function RemoteProvider({ getService }: FtrProviderContext) { const lifecycle = getService('lifecycle'); const log = getService('log'); const config = getService('config'); - const possibleBrowsers = ['chrome', 'firefox', 'ie']; const browserType = process.env.TEST_BROWSER_TYPE || 'chrome'; - if (!possibleBrowsers.includes(browserType)) { - throw new Error(`Unexpected TEST_BROWSER_TYPE "${browserType}". Valid options are ` + possibleBrowsers.join(',')); + if (browserType !== 'chrome' && browserType !== 'firefox') { + throw new Error( + `Unexpected TEST_BROWSER_TYPE "${browserType}", only "chrome" and "firefox" are supported` + ); } - const { driver, By, Key, until, LegacyActionSequence } = await initWebDriver({ log, browserType }); + const { driver, By, Key, until, LegacyActionSequence } = await initWebDriver(log, browserType); log.info('Remote initialized'); lifecycle.on('beforeTests', async () => { // hard coded default, can be overridden per suite using `browser.setWindowSize()` // and will be automatically reverted after each suite - await driver.manage().window().setRect({ width: 1600, height: 1000 }); + await driver + .manage() + .window() + .setRect({ width: 1600, height: 1000 }); }); - const windowSizeStack = []; + const windowSizeStack: Array<{ width: number; height: number }> = []; lifecycle.on('beforeTestSuite', async () => { - windowSizeStack.unshift(await driver.manage().window().getRect()); + windowSizeStack.unshift( + await driver + .manage() + .window() + .getRect() + ); }); lifecycle.on('beforeEachTest', async () => { @@ -50,8 +60,11 @@ export async function RemoteProvider({ getService }) { }); lifecycle.on('afterTestSuite', async () => { - const { width, height } = windowSizeStack.shift(); - await driver.manage().window().setRect({ width: width, height: height }); + const { width, height } = windowSizeStack.shift()!; + await driver + .manage() + .window() + .setRect({ width, height }); }); lifecycle.on('cleanup', async () => await driver.quit()); diff --git a/test/functional/services/remote/verbose_remote_logging.js b/test/functional/services/remote/verbose_remote_logging.js deleted file mode 100644 index 42ea4f7e00f85..0000000000000 --- a/test/functional/services/remote/verbose_remote_logging.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { green, magentaBright } from 'chalk'; - -export function initVerboseRemoteLogging(log, server) { - const wrap = (original, httpMethod) => (path, requestData, pathParts) => { - const url = '/' + path.split('/').slice(2).join('/').replace(/\$(\d)/, function (_, index) { - return encodeURIComponent(pathParts[index]); - }); - - if (requestData == null) { - log.verbose('[remote] > %s %s', httpMethod, url); - } else { - log.verbose('[remote] > %s %s %j', httpMethod, url, requestData); - } - - return original.call(server, path, requestData, pathParts) - .then(result => { - log.verbose(`[remote] < %s %s ${green('OK')}`, httpMethod, url); - return result; - }) - .catch(error => { - let message; - try { - message = JSON.parse(error.response.data).value.message; - } catch (err) { - message = err.message; - } - - log.verbose(`[remote] < %s %s ${magentaBright('ERR')} %j`, httpMethod, url, message.split(/\r?\n/)[0]); - throw error; - }); - }; - - server._get = wrap(server._get, 'GET'); - server._post = wrap(server._post, 'POST'); - server._delete = wrap(server._delete, 'DELETE'); - return server; -} diff --git a/test/functional/services/remote/webdriver.js b/test/functional/services/remote/webdriver.ts similarity index 62% rename from test/functional/services/remote/webdriver.js rename to test/functional/services/remote/webdriver.ts index 725d98bfbb25f..0be87382c94a0 100644 --- a/test/functional/services/remote/webdriver.js +++ b/test/functional/services/remote/webdriver.ts @@ -17,26 +17,31 @@ * under the License. */ +import { ToolingLog } from '@kbn/dev-utils'; import { delay } from 'bluebird'; -import { Builder, By, Key, until, logging } from 'selenium-webdriver'; -const { LegacyActionSequence } = require('selenium-webdriver/lib/actions'); -const { getLogger } = require('selenium-webdriver/lib/logging'); -const { Executor } = require('selenium-webdriver/lib/http'); -const chrome = require('selenium-webdriver/chrome'); -const firefox = require('selenium-webdriver/firefox'); -const geckoDriver = require('geckodriver'); -const chromeDriver = require('chromedriver'); -const throttleOption = process.env.TEST_THROTTLE_NETWORK; +import chromeDriver from 'chromedriver'; +// @ts-ignore types not available +import geckoDriver from 'geckodriver'; + +// @ts-ignore types for 4.0 not available yet +import { Builder, By, Key, logging, until } from 'selenium-webdriver'; +// @ts-ignore types not available +import chrome from 'selenium-webdriver/chrome'; +// @ts-ignore types not available +import firefox from 'selenium-webdriver/firefox'; +// @ts-ignore internal modules are not typed +import { LegacyActionSequence } from 'selenium-webdriver/lib/actions'; +// @ts-ignore internal modules are not typed +import { Executor } from 'selenium-webdriver/lib/http'; +// @ts-ignore internal modules are not typed +import { getLogger } from 'selenium-webdriver/lib/logging'; import { preventParallelCalls } from './prevent_parallel_calls'; +const throttleOption = process.env.TEST_THROTTLE_NETWORK; const SECOND = 1000; const MINUTE = 60 * SECOND; -const NO_QUEUE_COMMANDS = [ - 'getStatus', - 'newSession', - 'quit' -]; +const NO_QUEUE_COMMANDS = ['getStatus', 'newSession', 'quit']; /** * Best we can tell WebDriver locks up sometimes when we send too many @@ -46,24 +51,26 @@ const NO_QUEUE_COMMANDS = [ * queue all calls to Executor#send() if there is already a call in * progress. */ -Executor.prototype.execute = preventParallelCalls(Executor.prototype.execute, (command) => ( - NO_QUEUE_COMMANDS.includes(command.getName()) -)); +Executor.prototype.execute = preventParallelCalls( + Executor.prototype.execute, + (command: { getName: () => string }) => NO_QUEUE_COMMANDS.includes(command.getName()) +); let attemptCounter = 0; -async function attemptToCreateCommand(log, browserType) { +async function attemptToCreateCommand(log: ToolingLog, browserType: 'chrome' | 'firefox') { const attemptId = ++attemptCounter; log.debug('[webdriver] Creating session'); - const buildDriverInstance = async (browserType) => { + const buildDriverInstance = async () => { switch (browserType) { case 'chrome': const chromeOptions = new chrome.Options(); - const loggingPref = new logging.Preferences().setLevel(logging.Type.BROWSER, logging.Level.ALL); + const loggingPref = new logging.Preferences(); + loggingPref.setLevel(logging.Type.BROWSER, logging.Level.ALL); chromeOptions.setLoggingPrefs(loggingPref); if (process.env.TEST_BROWSER_HEADLESS) { - //Use --disable-gpu to avoid an error from a missing Mesa library, as per - //See: https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md + // Use --disable-gpu to avoid an error from a missing Mesa library, as per + // See: https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md chromeOptions.addArguments('headless', 'disable-gpu'); } return new Builder() @@ -74,7 +81,7 @@ async function attemptToCreateCommand(log, browserType) { case 'firefox': const firefoxOptions = new firefox.Options(); if (process.env.TEST_BROWSER_HEADLESS) { - //See: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode + // See: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode firefoxOptions.addArguments('-headless'); } return new Builder() @@ -87,31 +94,34 @@ async function attemptToCreateCommand(log, browserType) { } }; - const session = await buildDriverInstance(browserType); + const session = await buildDriverInstance(); - if (throttleOption === 'true' && browserType === 'chrome') { //Only chrome supports this option. + if (throttleOption === 'true' && browserType === 'chrome') { + // Only chrome supports this option. log.debug('NETWORK THROTTLED: 768k down, 256k up, 100ms latency.'); + session.setNetworkConditions({ offline: false, latency: 100, // Additional latency (ms). download_throughput: 768 * 1024, // These speeds are in bites per second, not kilobytes. - upload_throughput: 256 * 1024 + upload_throughput: 256 * 1024, }); } - if (attemptId !== attemptCounter) return; // abort + if (attemptId !== attemptCounter) { + return; + } // abort return { driver: session, By, Key, until, LegacyActionSequence }; } -export async function initWebDriver({ log, browserType }) { +export async function initWebDriver(log: ToolingLog, browserType: 'chrome' | 'firefox') { const logger = getLogger('webdriver.http.Executor'); logger.setLevel(logging.Level.FINEST); - logger.addHandler((entry) => { + logger.addHandler((entry: { message: string }) => { log.verbose(entry.message); }); - return await Promise.race([ (async () => { await delay(2 * MINUTE); @@ -122,7 +132,7 @@ export async function initWebDriver({ log, browserType }) { while (true) { const command = await Promise.race([ delay(30 * SECOND), - attemptToCreateCommand(log, browserType) + attemptToCreateCommand(log, browserType), ]); if (!command) { @@ -131,6 +141,6 @@ export async function initWebDriver({ log, browserType }) { return command; } - })() + })(), ]); } diff --git a/yarn.lock b/yarn.lock index d5c87e942fedc..043f85e1982f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1648,6 +1648,13 @@ resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.10.tgz#780d552467824be4a241b29510a7873a7432c4a6" integrity sha512-fOM/Jhv51iyugY7KOBZz2ThfT1gwvsGCfWxpLpZDgkGjpEO4Le9cld07OdskikLjDUQJ43dzDaVRSFwQlpdqVg== +"@types/chromedriver@^2.38.0": + version "2.38.0" + resolved "https://registry.yarnpkg.com/@types/chromedriver/-/chromedriver-2.38.0.tgz#971032b73eb7f44036f4f5bed59a7fd5b468014f" + integrity sha512-vcPGkZt1y2YVXKAY8SwCvU0u9mgw9+7tBV4HGb0YX/6bu1WXbb61bf8Y/N+xNCYwEj/Ug1UAMnhCcsSohXzRXw== + dependencies: + "@types/node" "*" + "@types/classnames@^2.2.3": version "2.2.3" resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.3.tgz#3f0ff6873da793870e20a260cada55982f38a9e5" @@ -7107,11 +7114,6 @@ core-js@^1.0.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= -core-js@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" - integrity sha1-+rg/uwstjchfpjbEudNMdUIMbWU= - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -8988,11 +8990,6 @@ es6-promise@^4.0.3, es6-promise@~4.2.4: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" integrity sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ== -es6-promise@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" - integrity sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y= - es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" @@ -14524,15 +14521,14 @@ jsx-ast-utils@^2.0.1: array-includes "^3.0.3" jszip@^3.1.3: - version "3.1.5" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.5.tgz#e3c2a6c6d706ac6e603314036d43cd40beefdf37" - integrity sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ== + version "3.2.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.2.0.tgz#1c179e8692777490ca4e9b8f3ced08f9b820da2c" + integrity sha512-4WjbsaEtBK/DHeDZOPiPw5nzSGLDEDDreFRDEgnoMwmknPjTqa+23XuYFk6NiGbeiAeZCctiQ/X/z0lQBmDVOQ== dependencies: - core-js "~2.3.0" - es6-promise "~3.0.2" - lie "~3.1.0" + lie "~3.3.0" pako "~1.0.2" - readable-stream "~2.0.6" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" just-extend@^1.1.27: version "1.1.27" @@ -14893,10 +14889,10 @@ license-checker@^16.0.0: spdx-satisfies "^0.1.3" treeify "^1.0.1" -lie@~3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" - integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4= +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== dependencies: immediate "~3.0.5" @@ -17594,9 +17590,9 @@ pako@^0.2.5: integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= pako@~1.0.2: - version "1.0.8" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.8.tgz#6844890aab9c635af868ad5fecc62e8acbba3ea4" - integrity sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA== + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== pako@~1.0.5: version "1.0.6" @@ -19644,7 +19640,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -19686,7 +19682,7 @@ readable-stream@~1.1.0, readable-stream@~1.1.9: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@~2.0.0, readable-stream@~2.0.6: +readable-stream@~2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= @@ -21042,7 +21038,7 @@ set-getter@^0.1.0: dependencies: to-object-path "^0.3.0" -set-immediate-shim@^1.0.0: +set-immediate-shim@^1.0.0, set-immediate-shim@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= From 5463a69d464ff1c65626ec360ca5fb1b56a615af Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Wed, 6 Mar 2019 09:30:24 -0800 Subject: [PATCH 09/27] Reenable auth provider tests (#32565) * Reenabling token auth provider test * Re-enabling saml test * Adding a SAML test to ensure we can't reuse refresh token after 60 seconds * Adding token auth provider re-use refresh token after 60 seconds test * Removing the reusuing refresh tokens after timeout --- .../apis/security/saml_login.js | 105 +++++------------ .../token_api_integration/auth/session.js | 110 ++++++++++-------- 2 files changed, 92 insertions(+), 123 deletions(-) diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.js b/x-pack/test/saml_api_integration/apis/security/saml_login.js index 3bb222954130a..fe6f2bf07db02 100644 --- a/x-pack/test/saml_api_integration/apis/security/saml_login.js +++ b/x-pack/test/saml_api_integration/apis/security/saml_login.js @@ -423,7 +423,7 @@ export default function ({ getService }) { }); }); - describe.skip('API access with expired access token.', () => { + describe('API access with expired access token.', () => { let sessionCookie; beforeEach(async () => { @@ -442,6 +442,14 @@ export default function ({ getService }) { sessionCookie = request.cookie(samlAuthenticationResponse.headers['set-cookie'][0]); }); + const expectNewSessionCookie = (cookie) => { + expect(cookie.key).to.be('sid'); + expect(cookie.value).to.not.be.empty(); + expect(cookie.path).to.be('/'); + expect(cookie.httpOnly).to.be(true); + expect(cookie.value).to.not.be(sessionCookie.value); + }; + it('expired access token should be automatically refreshed', async function () { this.timeout(40000); @@ -451,103 +459,46 @@ export default function ({ getService }) { // This api call should succeed and automatically refresh token. Returned cookie will contain // the new access and refresh token pair. - const apiResponse = await supertest + const firstResponse = await supertest .get('/api/security/v1/me') .set('kbn-xsrf', 'xxx') .set('Cookie', sessionCookie.cookieString()) .expect(200); - const cookies = apiResponse.headers['set-cookie']; - expect(cookies).to.have.length(1); + const firstResponseCookies = firstResponse.headers['set-cookie']; + expect(firstResponseCookies).to.have.length(1); - const newSessionCookie = request.cookie(cookies[0]); - expect(newSessionCookie.key).to.be('sid'); - expect(newSessionCookie.value).to.not.be.empty(); - expect(newSessionCookie.path).to.be('/'); - expect(newSessionCookie.httpOnly).to.be(true); - expect(newSessionCookie.value).to.not.be(sessionCookie.value); + const firstNewCookie = request.cookie(firstResponseCookies[0]); + expectNewSessionCookie(firstNewCookie); - // Request with old cookie should fail with `400` since it contains expired access token and - // already used refresh tokens. - const apiResponseWithExpiredToken = await supertest + // Request with old cookie should reuse the same refresh token if within 60 seconds. + // Returned cookie will contain the same new access and refresh token pairs as the first request + const secondResponse = await supertest .get('/api/security/v1/me') .set('kbn-xsrf', 'xxx') .set('Cookie', sessionCookie.cookieString()) - .expect(400); - expect(apiResponseWithExpiredToken.headers['set-cookie']).to.be(undefined); - expect(apiResponseWithExpiredToken.body).to.eql({ - error: 'Bad Request', - message: 'Both access and refresh tokens are expired.', - statusCode: 400 - }); - - // The new cookie with fresh pair of access and refresh tokens should work. - await supertest - .get('/api/security/v1/me') - .set('kbn-xsrf', 'xxx') - .set('Cookie', newSessionCookie.cookieString()) .expect(200); - }); - it('expired access token should be automatically refreshed with two concurrent requests', async function () { - this.timeout(40000); + const secondResponseCookies = secondResponse.headers['set-cookie']; + expect(secondResponseCookies).to.have.length(1); - // Access token expiration is set to 15s for API integration tests. - // Let's wait for 20s to make sure token expires. - await delay(20000); + const secondNewCookie = request.cookie(secondResponseCookies[0]); + expectNewSessionCookie(secondNewCookie); - // Issue two concurrent requests with the same cookie that contains expired access token. - // First request that uses refresh token should succeed, the second should fail since refresh - // token is one-time use only token. - const apiResponseOnePromise = supertest - .get('/api/security/v1/me') - .set('kbn-xsrf', 'xxx') - .set('Cookie', sessionCookie.cookieString()) - .expect(200); + expect(firstNewCookie.value).not.to.eql(secondNewCookie.value); - const apiResponseTwoPromise = supertest - .get('/api/security/v1/me') - .set('kbn-xsrf', 'xxx') - .set('Cookie', sessionCookie.cookieString()) - .expect(400); - - const apiResponseOne = await apiResponseOnePromise; - const cookies = apiResponseOne.headers['set-cookie']; - expect(cookies).to.have.length(1); - - const newSessionCookie = request.cookie(cookies[0]); - expect(newSessionCookie.key).to.be('sid'); - expect(newSessionCookie.value).to.not.be.empty(); - expect(newSessionCookie.path).to.be('/'); - expect(newSessionCookie.httpOnly).to.be(true); - expect(newSessionCookie.value).to.not.be(sessionCookie.value); - - const apiResponseTwo = await apiResponseTwoPromise; - expect(apiResponseTwo.headers['set-cookie']).to.be(undefined); - expect(apiResponseTwo.body).to.eql({ - error: 'Bad Request', - message: 'Both access and refresh tokens are expired.', - statusCode: 400 - }); - - // Request with old cookie should fail with `400` since it contains expired access token and - // already used refresh tokens. - const apiResponseWithExpiredToken = await supertest + // The first new cookie with fresh pair of access and refresh tokens should work. + await supertest .get('/api/security/v1/me') .set('kbn-xsrf', 'xxx') - .set('Cookie', sessionCookie.cookieString()) - .expect(400); - expect(apiResponseWithExpiredToken.body).to.eql({ - error: 'Bad Request', - message: 'Both access and refresh tokens are expired.', - statusCode: 400 - }); + .set('Cookie', firstNewCookie.cookieString()) + .expect(200); - // The new cookie with fresh pair of access and refresh tokens should work. + // The second new cookie with fresh pair of access and refresh tokens should work. await supertest .get('/api/security/v1/me') .set('kbn-xsrf', 'xxx') - .set('Cookie', newSessionCookie.cookieString()) + .set('Cookie', secondNewCookie.cookieString()) .expect(200); }); }); diff --git a/x-pack/test/token_api_integration/auth/session.js b/x-pack/test/token_api_integration/auth/session.js index 02610cdbe41c1..953873a615f53 100644 --- a/x-pack/test/token_api_integration/auth/session.js +++ b/x-pack/test/token_api_integration/auth/session.js @@ -6,6 +6,8 @@ import request from 'request'; +const delay = ms => new Promise(resolve => setTimeout(() => resolve(), ms)); + export default function ({ getService }) { const supertest = getService('supertestWithoutAuth'); @@ -57,52 +59,68 @@ export default function ({ getService }) { .expect(200); }); - it.skip('expired access token should be automatically refreshed', async function () { - this.timeout(40000); - - const originalCookie = await createSessionCookie(); - - // Access token expiration is set to 15s for API integration tests. - // Let's wait for 20s to make sure token expires. - await new Promise(resolve => setTimeout(() => resolve(), 20000)); - - // This api call should succeed and automatically refresh token. Returned cookie will contain - // the new access and refresh token pair. - const response = await supertest - .get('/api/security/v1/me') - .set('kbn-xsrf', 'true') - .set('cookie', originalCookie.cookieString()) - .expect(200); - - const newCookie = extractSessionCookie(response); - if (!newCookie) { - throw new Error('No session cookie set after token refresh'); - } - if (!newCookie.httpOnly) { - throw new Error('Session cookie is not marked as HttpOnly'); - } - if (newCookie.value === originalCookie.value) { - throw new Error('Session cookie has not changed after refresh'); - } - - // Request with old cookie should fail with `400` since it contains expired access token and - // already used refresh tokens. - const apiResponseWithExpiredToken = await supertest - .get('/api/security/v1/me') - .set('kbn-xsrf', 'true') - .set('Cookie', originalCookie.cookieString()) - .expect(400); - - if (apiResponseWithExpiredToken.headers['set-cookie'] !== undefined) { - throw new Error('Request rejecting expired access token still set session cookie'); - } - - // The new cookie with fresh pair of access and refresh tokens should work. - await supertest - .get('/api/security/v1/me') - .set('kbn-xsrf', 'true') - .set('Cookie', newCookie.cookieString()) - .expect(200); + describe('API access with expired access token.', function () { + const expectNewSessionCookie = (originalCookie, newCookie) => { + if (!newCookie) { + throw new Error('No session cookie set after token refresh'); + } + if (!newCookie.httpOnly) { + throw new Error('Session cookie is not marked as HttpOnly'); + } + if (newCookie.value === originalCookie.value) { + throw new Error('Session cookie has not changed after refresh'); + } + }; + + it('expired access token should be automatically refreshed', async function () { + this.timeout(40000); + + const originalCookie = await createSessionCookie(); + + // Access token expiration is set to 15s for API integration tests. + // Let's wait for 20s to make sure token expires. + await delay(20000); + + // This api call should succeed and automatically refresh token. Returned cookie will contain + // the new access and refresh token pair. + const firstResponse = await supertest + .get('/api/security/v1/me') + .set('kbn-xsrf', 'true') + .set('cookie', originalCookie.cookieString()) + .expect(200); + + const firstNewCookie = extractSessionCookie(firstResponse); + expectNewSessionCookie(originalCookie, firstNewCookie); + + // Request with old cookie should return another valid cookie we can use to authenticate requests + // if it happens within 60 seconds of the refresh token being used + const secondResponse = await supertest + .get('/api/security/v1/me') + .set('kbn-xsrf', 'true') + .set('Cookie', originalCookie.cookieString()) + .expect(200); + + const secondNewCookie = extractSessionCookie(secondResponse); + expectNewSessionCookie(originalCookie, secondNewCookie); + + if (secondNewCookie.value === firstNewCookie.value) { + throw new Error('Second new cookie is the same as the first new cookie'); + } + + // The first new cookie should authenticate a subsequent request + await supertest + .get('/api/security/v1/me') + .set('kbn-xsrf', 'true') + .set('Cookie', firstNewCookie.cookieString()) + .expect(200); + + // The second new cookie should authenticate a subsequent request + await supertest + .get('/api/security/v1/me') + .set('kbn-xsrf', 'true') + .set('Cookie', secondNewCookie.cookieString()) + .expect(200); + }); }); }); } From 610495bee97506feb3fa921674131eb7818b780a Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Wed, 6 Mar 2019 17:32:30 +0000 Subject: [PATCH 10/27] [ML] Fix chart tooltip positioning for new K7 navigation (#32563) * [ML] Fix chart tooltip positioning for new K7 navigation * [ML] Move numeric offset numbers into variables --- .../chart_tooltip/chart_tooltip_service.js | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/ml/public/components/chart_tooltip/chart_tooltip_service.js b/x-pack/plugins/ml/public/components/chart_tooltip/chart_tooltip_service.js index 285fe62003c64..693676f275a18 100644 --- a/x-pack/plugins/ml/public/components/chart_tooltip/chart_tooltip_service.js +++ b/x-pack/plugins/ml/public/components/chart_tooltip/chart_tooltip_service.js @@ -32,30 +32,27 @@ mlChartTooltipService.show = function (contents, target, offset = { x: 0, y: 0 } this.element.html(contents); // side bar width - const navOffset = $('.kbnGlobalNav').width() || 0; - const contentWidth = $('body').width() - navOffset - 10; + const navOffset = $('.euiNavDrawer').width() || 0; // Offset by width of side navbar + const contentWidth = $('body').width() - navOffset; const tooltipWidth = this.element.width(); - const scrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); const pos = target.getBoundingClientRect(); - const x = (pos.left + (offset.x) + 4) - navOffset; - const y = pos.top + (offset.y) + scrollTop; - - if (x + tooltipWidth > contentWidth) { + let left = (pos.left + (offset.x) + 4) - navOffset; + if (left + tooltipWidth > contentWidth) { // the tooltip is hanging off the side of the page, // so move it to the other side of the target - this.element.css({ - left: x - (tooltipWidth + offset.x + 22), - top: (y - 28) - }); - } else { - this.element.css({ - left: x, - top: (y - 28) - }); + const markerWidthAdjustment = 22; + left = left - (tooltipWidth + offset.x + markerWidthAdjustment); } + // Calculate top offset + const scrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); + const topNavHeightAdjustment = 75; + const top = pos.top + (offset.y) + scrollTop - topNavHeightAdjustment; + this.element.css({ + left, + top, opacity: '0.9', display: 'block' }); From e1dcdc811950968285dc5d1fb4e1f6c93a425a3e Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 6 Mar 2019 09:41:33 -0800 Subject: [PATCH 11/27] [DOCS] Adds tagged region for notable breaking changes (#30155) --- docs/migration/migrate_8_0.asciidoc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/migration/migrate_8_0.asciidoc b/docs/migration/migrate_8_0.asciidoc index caf1b3dab037b..bf5857ab814c3 100644 --- a/docs/migration/migrate_8_0.asciidoc +++ b/docs/migration/migrate_8_0.asciidoc @@ -10,3 +10,12 @@ your application to Kibana 8.0. coming[8.0.0] See also <> and <>. + +//// +The following section is re-used in the Installation and Upgrade Guide +[[breaking_80_notable]] +=== Notable breaking changes +//// +// tag::notable-breaking-changes[] + +// end::notable-breaking-changes[] \ No newline at end of file From 191c2f43b620184161587d608ed8ee6edc444537 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 6 Mar 2019 11:45:41 -0800 Subject: [PATCH 12/27] [dashboard/ftr] disable panelCount assertions (#32579) Chatted with @markov00 and he suggests that we can disable the `dashboardExpect.panelCount()` assertions for now to avoid triggering #28818. I'll work with @dmlemeshko and @markov00 to get a PR up that is heavily testing this suite and try nailing down what is causing this rendering disparity. --- test/functional/apps/dashboard/_embeddable_rendering.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/dashboard/_embeddable_rendering.js b/test/functional/apps/dashboard/_embeddable_rendering.js index d3a480cddf9c7..f39d9ab4377c3 100644 --- a/test/functional/apps/dashboard/_embeddable_rendering.js +++ b/test/functional/apps/dashboard/_embeddable_rendering.js @@ -113,7 +113,7 @@ export default function ({ getService, getPageObjects }) { await dashboardAddPanel.addVisualization('Filter Bytes Test: vega'); await PageObjects.header.waitUntilLoadingHasFinished(); - await dashboardExpect.panelCount(27); + // await dashboardExpect.panelCount(27); await PageObjects.dashboard.waitForRenderComplete(); }); @@ -121,7 +121,7 @@ export default function ({ getService, getPageObjects }) { await dashboardAddPanel.addEverySavedSearch('"Rendering Test"'); await dashboardAddPanel.closeAddPanel(); await PageObjects.header.waitUntilLoadingHasFinished(); - await dashboardExpect.panelCount(28); + // await dashboardExpect.panelCount(28); await PageObjects.dashboard.waitForRenderComplete(); await PageObjects.dashboard.saveDashboard('embeddable rendering test', { storeTimeWithDashboard: true }); From 4e4b7737299c5204fb5c2153c780f909e683c826 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 6 Mar 2019 13:08:39 -0800 Subject: [PATCH 13/27] [dashboard/ftr] remove _-prefix from file names (#32585) --- ..._bwc_shared_urls.js => bwc_shared_urls.js} | 0 ...ables.js => create_and_add_embeddables.js} | 0 ..._dashboard_clone.js => dashboard_clone.js} | 0 ..._filter_bar.js => dashboard_filter_bar.js} | 0 ...rd_filtering.js => dashboard_filtering.js} | 0 .../{_dashboard_grid.js => dashboard_grid.js} | 0 ...hboard_listing.js => dashboard_listing.js} | 0 ...hboard_options.js => dashboard_options.js} | 0 ...rd_query_bar.js => dashboard_query_bar.js} | 0 .../{_dashboard_save.js => dashboard_save.js} | 0 ...rd_snapshots.js => dashboard_snapshots.js} | 0 ..._dashboard_state.js => dashboard_state.js} | 0 .../{_dashboard_time.js => dashboard_time.js} | 0 ...ime_picker.js => dashboard_time_picker.js} | 0 ...ttributes.js => data_shared_attributes.js} | 0 .../{_embed_mode.js => embed_mode.js} | 0 ...e_rendering.js => embeddable_rendering.js} | 0 ..._empty_dashboard.js => empty_dashboard.js} | 0 ...ull_screen_mode.js => full_screen_mode.js} | 0 test/functional/apps/dashboard/index.js | 46 +++++++++---------- .../{_panel_controls.js => panel_controls.js} | 0 ...xpand_toggle.js => panel_expand_toggle.js} | 0 .../{_time_zones.js => time_zones.js} | 0 .../dashboard/{_view_edit.js => view_edit.js} | 0 24 files changed, 23 insertions(+), 23 deletions(-) rename test/functional/apps/dashboard/{_bwc_shared_urls.js => bwc_shared_urls.js} (100%) rename test/functional/apps/dashboard/{_create_and_add_embeddables.js => create_and_add_embeddables.js} (100%) rename test/functional/apps/dashboard/{_dashboard_clone.js => dashboard_clone.js} (100%) rename test/functional/apps/dashboard/{_dashboard_filter_bar.js => dashboard_filter_bar.js} (100%) rename test/functional/apps/dashboard/{_dashboard_filtering.js => dashboard_filtering.js} (100%) rename test/functional/apps/dashboard/{_dashboard_grid.js => dashboard_grid.js} (100%) rename test/functional/apps/dashboard/{_dashboard_listing.js => dashboard_listing.js} (100%) rename test/functional/apps/dashboard/{_dashboard_options.js => dashboard_options.js} (100%) rename test/functional/apps/dashboard/{_dashboard_query_bar.js => dashboard_query_bar.js} (100%) rename test/functional/apps/dashboard/{_dashboard_save.js => dashboard_save.js} (100%) rename test/functional/apps/dashboard/{_dashboard_snapshots.js => dashboard_snapshots.js} (100%) rename test/functional/apps/dashboard/{_dashboard_state.js => dashboard_state.js} (100%) rename test/functional/apps/dashboard/{_dashboard_time.js => dashboard_time.js} (100%) rename test/functional/apps/dashboard/{_dashboard_time_picker.js => dashboard_time_picker.js} (100%) rename test/functional/apps/dashboard/{_data_shared_attributes.js => data_shared_attributes.js} (100%) rename test/functional/apps/dashboard/{_embed_mode.js => embed_mode.js} (100%) rename test/functional/apps/dashboard/{_embeddable_rendering.js => embeddable_rendering.js} (100%) rename test/functional/apps/dashboard/{_empty_dashboard.js => empty_dashboard.js} (100%) rename test/functional/apps/dashboard/{_full_screen_mode.js => full_screen_mode.js} (100%) rename test/functional/apps/dashboard/{_panel_controls.js => panel_controls.js} (100%) rename test/functional/apps/dashboard/{_panel_expand_toggle.js => panel_expand_toggle.js} (100%) rename test/functional/apps/dashboard/{_time_zones.js => time_zones.js} (100%) rename test/functional/apps/dashboard/{_view_edit.js => view_edit.js} (100%) diff --git a/test/functional/apps/dashboard/_bwc_shared_urls.js b/test/functional/apps/dashboard/bwc_shared_urls.js similarity index 100% rename from test/functional/apps/dashboard/_bwc_shared_urls.js rename to test/functional/apps/dashboard/bwc_shared_urls.js diff --git a/test/functional/apps/dashboard/_create_and_add_embeddables.js b/test/functional/apps/dashboard/create_and_add_embeddables.js similarity index 100% rename from test/functional/apps/dashboard/_create_and_add_embeddables.js rename to test/functional/apps/dashboard/create_and_add_embeddables.js diff --git a/test/functional/apps/dashboard/_dashboard_clone.js b/test/functional/apps/dashboard/dashboard_clone.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_clone.js rename to test/functional/apps/dashboard/dashboard_clone.js diff --git a/test/functional/apps/dashboard/_dashboard_filter_bar.js b/test/functional/apps/dashboard/dashboard_filter_bar.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_filter_bar.js rename to test/functional/apps/dashboard/dashboard_filter_bar.js diff --git a/test/functional/apps/dashboard/_dashboard_filtering.js b/test/functional/apps/dashboard/dashboard_filtering.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_filtering.js rename to test/functional/apps/dashboard/dashboard_filtering.js diff --git a/test/functional/apps/dashboard/_dashboard_grid.js b/test/functional/apps/dashboard/dashboard_grid.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_grid.js rename to test/functional/apps/dashboard/dashboard_grid.js diff --git a/test/functional/apps/dashboard/_dashboard_listing.js b/test/functional/apps/dashboard/dashboard_listing.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_listing.js rename to test/functional/apps/dashboard/dashboard_listing.js diff --git a/test/functional/apps/dashboard/_dashboard_options.js b/test/functional/apps/dashboard/dashboard_options.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_options.js rename to test/functional/apps/dashboard/dashboard_options.js diff --git a/test/functional/apps/dashboard/_dashboard_query_bar.js b/test/functional/apps/dashboard/dashboard_query_bar.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_query_bar.js rename to test/functional/apps/dashboard/dashboard_query_bar.js diff --git a/test/functional/apps/dashboard/_dashboard_save.js b/test/functional/apps/dashboard/dashboard_save.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_save.js rename to test/functional/apps/dashboard/dashboard_save.js diff --git a/test/functional/apps/dashboard/_dashboard_snapshots.js b/test/functional/apps/dashboard/dashboard_snapshots.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_snapshots.js rename to test/functional/apps/dashboard/dashboard_snapshots.js diff --git a/test/functional/apps/dashboard/_dashboard_state.js b/test/functional/apps/dashboard/dashboard_state.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_state.js rename to test/functional/apps/dashboard/dashboard_state.js diff --git a/test/functional/apps/dashboard/_dashboard_time.js b/test/functional/apps/dashboard/dashboard_time.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_time.js rename to test/functional/apps/dashboard/dashboard_time.js diff --git a/test/functional/apps/dashboard/_dashboard_time_picker.js b/test/functional/apps/dashboard/dashboard_time_picker.js similarity index 100% rename from test/functional/apps/dashboard/_dashboard_time_picker.js rename to test/functional/apps/dashboard/dashboard_time_picker.js diff --git a/test/functional/apps/dashboard/_data_shared_attributes.js b/test/functional/apps/dashboard/data_shared_attributes.js similarity index 100% rename from test/functional/apps/dashboard/_data_shared_attributes.js rename to test/functional/apps/dashboard/data_shared_attributes.js diff --git a/test/functional/apps/dashboard/_embed_mode.js b/test/functional/apps/dashboard/embed_mode.js similarity index 100% rename from test/functional/apps/dashboard/_embed_mode.js rename to test/functional/apps/dashboard/embed_mode.js diff --git a/test/functional/apps/dashboard/_embeddable_rendering.js b/test/functional/apps/dashboard/embeddable_rendering.js similarity index 100% rename from test/functional/apps/dashboard/_embeddable_rendering.js rename to test/functional/apps/dashboard/embeddable_rendering.js diff --git a/test/functional/apps/dashboard/_empty_dashboard.js b/test/functional/apps/dashboard/empty_dashboard.js similarity index 100% rename from test/functional/apps/dashboard/_empty_dashboard.js rename to test/functional/apps/dashboard/empty_dashboard.js diff --git a/test/functional/apps/dashboard/_full_screen_mode.js b/test/functional/apps/dashboard/full_screen_mode.js similarity index 100% rename from test/functional/apps/dashboard/_full_screen_mode.js rename to test/functional/apps/dashboard/full_screen_mode.js diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js index 6919ffc75eb06..6ddbce1ba6bf2 100644 --- a/test/functional/apps/dashboard/index.js +++ b/test/functional/apps/dashboard/index.js @@ -46,18 +46,18 @@ export default function ({ getService, loadTestFile, getPageObjects }) { before(loadCurrentData); after(unloadCurrentData); - loadTestFile(require.resolve('./_empty_dashboard')); - loadTestFile(require.resolve('./_embeddable_rendering')); - loadTestFile(require.resolve('./_create_and_add_embeddables')); - loadTestFile(require.resolve('./_time_zones')); - loadTestFile(require.resolve('./_dashboard_options')); - loadTestFile(require.resolve('./_data_shared_attributes')); - loadTestFile(require.resolve('./_embed_mode')); + loadTestFile(require.resolve('./empty_dashboard')); + loadTestFile(require.resolve('./embeddable_rendering')); + loadTestFile(require.resolve('./create_and_add_embeddables')); + loadTestFile(require.resolve('./time_zones')); + loadTestFile(require.resolve('./dashboard_options')); + loadTestFile(require.resolve('./data_shared_attributes')); + loadTestFile(require.resolve('./embed_mode')); // Note: This one must be last because it unloads some data for one of its tests! // No, this isn't ideal, but loading/unloading takes so much time and these are all bunched // to improve efficiency... - loadTestFile(require.resolve('./_dashboard_query_bar')); + loadTestFile(require.resolve('./dashboard_query_bar')); }); describe('using current data', function () { @@ -65,13 +65,13 @@ export default function ({ getService, loadTestFile, getPageObjects }) { before(loadCurrentData); after(unloadCurrentData); - loadTestFile(require.resolve('./_full_screen_mode')); - loadTestFile(require.resolve('./_dashboard_filter_bar')); - loadTestFile(require.resolve('./_dashboard_filtering')); - loadTestFile(require.resolve('./_panel_expand_toggle')); - loadTestFile(require.resolve('./_dashboard_grid')); - loadTestFile(require.resolve('./_dashboard_snapshots')); - loadTestFile(require.resolve('./_view_edit')); + loadTestFile(require.resolve('./full_screen_mode')); + loadTestFile(require.resolve('./dashboard_filter_bar')); + loadTestFile(require.resolve('./dashboard_filtering')); + loadTestFile(require.resolve('./panel_expand_toggle')); + loadTestFile(require.resolve('./dashboard_grid')); + loadTestFile(require.resolve('./dashboard_snapshots')); + loadTestFile(require.resolve('./view_edit')); }); // Each of these tests call initTests themselves, the way it was originally written. The above tests only load @@ -81,20 +81,20 @@ export default function ({ getService, loadTestFile, getPageObjects }) { this.tags('ciGroup4'); before(() => browser.setWindowSize(1200, 900)); - loadTestFile(require.resolve('./_dashboard_time_picker')); - loadTestFile(require.resolve('./_bwc_shared_urls')); - loadTestFile(require.resolve('./_panel_controls')); - loadTestFile(require.resolve('./_dashboard_state')); + loadTestFile(require.resolve('./dashboard_time_picker')); + loadTestFile(require.resolve('./bwc_shared_urls')); + loadTestFile(require.resolve('./panel_controls')); + loadTestFile(require.resolve('./dashboard_state')); }); describe('using legacy data', function () { this.tags('ciGroup5'); before(() => browser.setWindowSize(1200, 900)); - loadTestFile(require.resolve('./_dashboard_save')); - loadTestFile(require.resolve('./_dashboard_time')); - loadTestFile(require.resolve('./_dashboard_listing')); - loadTestFile(require.resolve('./_dashboard_clone')); + loadTestFile(require.resolve('./dashboard_save')); + loadTestFile(require.resolve('./dashboard_time')); + loadTestFile(require.resolve('./dashboard_listing')); + loadTestFile(require.resolve('./dashboard_clone')); }); }); } diff --git a/test/functional/apps/dashboard/_panel_controls.js b/test/functional/apps/dashboard/panel_controls.js similarity index 100% rename from test/functional/apps/dashboard/_panel_controls.js rename to test/functional/apps/dashboard/panel_controls.js diff --git a/test/functional/apps/dashboard/_panel_expand_toggle.js b/test/functional/apps/dashboard/panel_expand_toggle.js similarity index 100% rename from test/functional/apps/dashboard/_panel_expand_toggle.js rename to test/functional/apps/dashboard/panel_expand_toggle.js diff --git a/test/functional/apps/dashboard/_time_zones.js b/test/functional/apps/dashboard/time_zones.js similarity index 100% rename from test/functional/apps/dashboard/_time_zones.js rename to test/functional/apps/dashboard/time_zones.js diff --git a/test/functional/apps/dashboard/_view_edit.js b/test/functional/apps/dashboard/view_edit.js similarity index 100% rename from test/functional/apps/dashboard/_view_edit.js rename to test/functional/apps/dashboard/view_edit.js From 5c5675bf43b20c8d313a8b412ef94d1238069198 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 6 Mar 2019 13:11:07 -0800 Subject: [PATCH 14/27] [ftr] determine oss ciGroups by reading the yaml file (#32575) --- .ci/jobs.yml | 1 - tasks/function_test_groups.js | 27 +++++++++------------------ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/.ci/jobs.yml b/.ci/jobs.yml index 84d8f75acbd30..596f22215a2a6 100644 --- a/.ci/jobs.yml +++ b/.ci/jobs.yml @@ -1,7 +1,6 @@ JOB: - kibana-intake - x-pack-intake - # make sure all kibana-ciGRoups are listed in tasks/function_test_groups.js - kibana-ciGroup1 - kibana-ciGroup2 - kibana-ciGroup3 diff --git a/tasks/function_test_groups.js b/tasks/function_test_groups.js index be6ce3ec67f5f..6992300bad53b 100644 --- a/tasks/function_test_groups.js +++ b/tasks/function_test_groups.js @@ -17,27 +17,18 @@ * under the License. */ +import { readFileSync } from 'fs'; +import { resolve } from 'path'; + import execa from 'execa'; import grunt from 'grunt'; +import { safeLoad } from 'js-yaml'; -/** - * The list of tags that we use in the functional tests, if we add a new group we need to add it to this list - * and to the list of jobs in .ci/jobs.yml - */ -const TEST_TAGS = [ - 'ciGroup1', - 'ciGroup2', - 'ciGroup3', - 'ciGroup4', - 'ciGroup5', - 'ciGroup6', - 'ciGroup7', - 'ciGroup8', - 'ciGroup9', - 'ciGroup10', - 'ciGroup11', - 'ciGroup12' -]; +const JOBS_YAML = readFileSync(resolve(__dirname, '../.ci/jobs.yml'), 'utf8'); +const TEST_TAGS = safeLoad(JOBS_YAML) + .JOB + .filter(id => id.startsWith('kibana-ciGroup')) + .map(id => id.replace(/^kibana-/, '')); export function getFunctionalTestGroupRunConfigs({ esFrom, kibanaInstallDir } = {}) { return { From f801954dca0cf5d47b48201b0932f6fc690261fd Mon Sep 17 00:00:00 2001 From: Fuyao Zhao Date: Wed, 6 Mar 2019 14:04:23 -0800 Subject: [PATCH 15/27] Be able to delete watch_optimizer_cache_state.json when data folder is outside of Kibana source folder (#32446) --- src/optimize/watch/watch_cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optimize/watch/watch_cache.ts b/src/optimize/watch/watch_cache.ts index 5b594b30e9f8f..01afed1b2640f 100644 --- a/src/optimize/watch/watch_cache.ts +++ b/src/optimize/watch/watch_cache.ts @@ -92,7 +92,7 @@ export class WatchCache { // start by deleting the state file to lower the // amount of time that another process might be able to // successfully read it once we decide to delete it - await del(this.statePath); + await del(this.statePath, { force: true }); // delete everything in optimize/.cache directory // except ts-node From f74b70ff4eaaa1b2dd8608318ff66318a447db5a Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Wed, 6 Mar 2019 14:52:22 -0800 Subject: [PATCH 16/27] Fix common router request handler issue (#32275) * Add `await` to common router request handler * Remove once() from common license checker and call with request factories --- .../call_with_request_factory.js | 10 ++-------- x-pack/server/lib/create_router/index.js | 6 +++--- .../license_pre_routing_factory.js | 5 +---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/x-pack/server/lib/create_router/call_with_request_factory/call_with_request_factory.js b/x-pack/server/lib/create_router/call_with_request_factory/call_with_request_factory.js index 7359a831994f9..a7703335f0c90 100644 --- a/x-pack/server/lib/create_router/call_with_request_factory/call_with_request_factory.js +++ b/x-pack/server/lib/create_router/call_with_request_factory/call_with_request_factory.js @@ -4,15 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { once } from 'lodash'; - -const callWithRequest = once(server => { - const cluster = server.plugins.elasticsearch.getCluster('data'); - return cluster.callWithRequest; -}); - export const callWithRequestFactory = (server, request) => { + const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); return (...args) => { - return callWithRequest(server)(request, ...args); + return callWithRequest(request, ...args); }; }; diff --git a/x-pack/server/lib/create_router/index.js b/x-pack/server/lib/create_router/index.js index 56353dc45fe74..74ec7dc936740 100644 --- a/x-pack/server/lib/create_router/index.js +++ b/x-pack/server/lib/create_router/index.js @@ -16,13 +16,13 @@ export const createRouter = (server, pluginId, apiBasePath = '') => { const requestHandler = (handler) => async (request, h) => { const callWithRequest = callWithRequestFactory(server, request); try { - return handler(request, callWithRequest, h); + return await handler(request, callWithRequest, h); } catch (err) { if (isEsError(err)) { - throw wrapEsError(err); + return wrapEsError(err); } - throw wrapUnknownError(err); + return wrapUnknownError(err); } }; diff --git a/x-pack/server/lib/create_router/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/server/lib/create_router/license_pre_routing_factory/license_pre_routing_factory.js index de951d40c0b98..43ff9aa231889 100644 --- a/x-pack/server/lib/create_router/license_pre_routing_factory/license_pre_routing_factory.js +++ b/x-pack/server/lib/create_router/license_pre_routing_factory/license_pre_routing_factory.js @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { once } from 'lodash'; import { wrapCustomError } from '../error_wrappers'; import { LICENSE_STATUS_VALID } from '../../../../common/constants'; export const licensePreRoutingFactory = (server, pluginId) => { - const licensePreRouting = () => { + return () => { // License checking and enable/disable logic const xpackMainPlugin = server.plugins.xpack_main; const licenseCheckResults = xpackMainPlugin.info.feature(pluginId).getLicenseCheckResults(); @@ -21,6 +20,4 @@ export const licensePreRoutingFactory = (server, pluginId) => { return null; }; - - return () => once(licensePreRouting)(); }; From ed3a807159e6a58d5449efd6c4ece378a63796c0 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 6 Mar 2019 15:03:17 -0800 Subject: [PATCH 17/27] Fix API endpoint typo indices/clear_caches -> indices/clear_cache. (#32582) --- .../server/routes/api/indices/register_clear_cache_route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.js b/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.js index 95afc6c1c5382..5c003ebe5bb64 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.js +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.js @@ -17,5 +17,5 @@ const handler = async (request, callWithRequest, h) => { return h.response(); }; export function registerClearCacheRoute(router) { - router.post('indices/clear_caches', handler); + router.post('indices/clear_cache', handler); } From d1d26db054ea3523082f290984a40d3160588f1a Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Wed, 6 Mar 2019 18:24:41 -0500 Subject: [PATCH 18/27] [Maps] Localization (#30881) Localize Maps-app UX with Kibana's i18n framework. --- .i18nrc.json | 1 + x-pack/plugins/maps/common/constants.js | 4 + x-pack/plugins/maps/common/i18n_getters.js | 26 +++++ x-pack/plugins/maps/index.js | 25 +++-- .../maps/public/angular/map_controller.js | 45 ++++++--- .../public/components/layer_addpanel/view.js | 30 +++++- .../__snapshots__/view.test.js.snap | 6 +- .../layer_panel/flyout_footer/view.js | 24 ++++- .../layer_panel/join_editor/resources/join.js | 16 +++- .../join_editor/resources/join_expression.js | 55 ++++++++--- .../resources/metrics_expression.js | 28 ++++-- .../layer_panel/join_editor/view.js | 26 ++++- .../settings_panel/settings_panel.js | 43 +++++++-- .../public/components/layer_panel/view.js | 24 ++++- .../components/layer_panel/view.test.js | 6 +- .../attribution_control/view.test.js | 4 +- .../__snapshots__/view.test.js.snap | 18 +++- .../layer_toc/toc_entry/view.test.js | 6 +- .../widget_overlay/layer_control/view.js | 13 ++- .../widget_overlay/layer_control/view.test.js | 6 +- .../view_control/view_control.js | 20 +++- .../maps/public/elasticsearch_geo_utils.js | 33 +++++-- .../public/inspector/views/map_details.js | 26 ++++- .../maps/public/inspector/views/map_view.js | 9 +- x-pack/plugins/maps/public/meta.js | 6 +- .../plugins/maps/public/register_feature.js | 14 ++- .../shared/components/layer_toc_actions.js | 29 ++++-- .../components/layer_toc_actions.test.js | 14 +-- .../public/shared/components/map_listing.js | 96 +++++++++++++++---- .../public/shared/components/metric_select.js | 33 +++++-- .../shared/components/metrics_editor.js | 22 ++++- .../components/no_index_pattern_callout.js | 35 +++++-- .../shared/components/validated_range.js | 7 +- .../public/shared/layers/heatmap_layer.js | 5 +- .../ems_file_source/create_source_editor.js | 13 ++- .../ems_file_source/ems_file_source.js | 29 +++++- .../ems_tms_source/create_source_editor.js | 5 +- .../sources/ems_tms_source/ems_tms_source.js | 27 +++++- .../layers/sources/ems_unavailable_message.js | 17 ++-- .../create_source_editor.js | 40 ++++++-- .../es_geo_grid_source/es_geo_grid_source.js | 46 +++++++-- .../es_geo_grid_source/resolution_editor.js | 28 +++++- .../update_source_editor.js | 8 +- .../shared/layers/sources/es_join_source.js | 27 +++++- .../es_search_source/create_source_editor.js | 24 +++-- .../es_search_source/es_search_source.js | 36 +++++-- .../es_search_source/update_source_editor.js | 26 ++++- .../public/shared/layers/sources/es_source.js | 16 +++- .../create_source_editor.js | 18 ++-- .../kibana_regionmap_source.js | 31 ++++-- .../create_source_editor.js | 18 ++-- .../kibana_tilemap_source.js | 27 +++++- .../shared/layers/sources/vector_source.js | 11 ++- .../shared/layers/sources/wms_source.js | 43 +++++++-- .../shared/layers/sources/xyz_tms_source.js | 16 +++- .../components/static_dynamic_style_row.js | 9 +- .../styles/components/vector/field_select.js | 7 +- .../vector/size/size_range_selector.js | 13 ++- .../components/vector/vector_style_editor.js | 25 ++++- .../shared/layers/styles/heatmap_style.js | 24 ++--- .../public/shared/layers/styles/tile_style.js | 5 +- .../shared/layers/styles/vector_style.js | 6 +- .../maps/public/shared/utils/color_utils.js | 5 +- .../sample_data/ecommerce_saved_objects.js | 64 +++++++++++++ .../sample_data/ecommerce_saved_objects.json | 51 ---------- .../sample_data/flights_saved_objects.js | 50 ++++++++++ .../sample_data/flights_saved_objects.json | 36 ------- .../sample_data/web_logs_saved_objects.js | 49 ++++++++++ .../sample_data/web_logs_saved_objects.json | 36 ------- 69 files changed, 1217 insertions(+), 424 deletions(-) create mode 100644 x-pack/plugins/maps/common/i18n_getters.js create mode 100644 x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js delete mode 100644 x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.json create mode 100644 x-pack/plugins/maps/server/sample_data/flights_saved_objects.js delete mode 100644 x-pack/plugins/maps/server/sample_data/flights_saved_objects.json create mode 100644 x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js delete mode 100644 x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.json diff --git a/.i18nrc.json b/.i18nrc.json index 4e942485d3aab..6527a06b3fb7b 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -31,6 +31,7 @@ "xpack.infra": "x-pack/plugins/infra", "xpack.kueryAutocomplete": "x-pack/plugins/kuery_autocomplete", "xpack.licenseMgmt": "x-pack/plugins/license_management", + "xpack.maps": "x-pack/plugins/maps", "xpack.ml": "x-pack/plugins/ml", "xpack.logstash": "x-pack/plugins/logstash", "xpack.main": "x-pack/plugins/xpack_main", diff --git a/x-pack/plugins/maps/common/constants.js b/x-pack/plugins/maps/common/constants.js index 99cf0b173776e..0af229569e5d1 100644 --- a/x-pack/plugins/maps/common/constants.js +++ b/x-pack/plugins/maps/common/constants.js @@ -16,4 +16,8 @@ export const ZOOM_PRECISION = 2; export const DEFAULT_EMS_TILE_LAYER = 'road_map'; +export const APP_ID = 'maps'; + +export const APP_ICON = 'gisApp'; + export const SOURCE_DATA_ID_ORIGIN = 'source'; diff --git a/x-pack/plugins/maps/common/i18n_getters.js b/x-pack/plugins/maps/common/i18n_getters.js new file mode 100644 index 0000000000000..0055c899e52c7 --- /dev/null +++ b/x-pack/plugins/maps/common/i18n_getters.js @@ -0,0 +1,26 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export function getAppTitle() { + return i18n.translate('xpack.maps.appTitle', { + defaultMessage: 'Maps' + }); +} + + +export function getDataSourceLabel() { + return i18n.translate('xpack.maps.source.dataSourceLabel', { + defaultMessage: 'Data source' + }); +} + +export function getUrlLabel() { + return i18n.translate('xpack.maps.source.urlLabel', { + defaultMessage: 'Url' + }); +} diff --git a/x-pack/plugins/maps/index.js b/x-pack/plugins/maps/index.js index ba6a4fcfd4b45..567e8c5e97deb 100644 --- a/x-pack/plugins/maps/index.js +++ b/x-pack/plugins/maps/index.js @@ -6,30 +6,35 @@ import { resolve } from 'path'; import { initRoutes } from './server/routes'; -import ecommerceSavedObjects from './server/sample_data/ecommerce_saved_objects.json'; -import fligthsSavedObjects from './server/sample_data/flights_saved_objects.json'; -import webLogsSavedObjects from './server/sample_data/web_logs_saved_objects.json'; +import { getEcommerceSavedObjects } from './server/sample_data/ecommerce_saved_objects'; +import { getFlightsSavedObjects } from './server/sample_data/flights_saved_objects.js'; +import { getWebLogsSavedObjects } from './server/sample_data/web_logs_saved_objects.js'; import mappings from './mappings.json'; import { checkLicense } from './check_license'; import { migrations } from './migrations'; import { watchStatusAndLicenseToInitialize } from '../../server/lib/watch_status_and_license_to_initialize'; import { initTelemetryCollection } from './server/maps_telemetry'; +import { i18n } from '@kbn/i18n'; +import { APP_ID, APP_ICON } from './common/constants'; +import { getAppTitle } from './common/i18n_getters'; export function maps(kibana) { return new kibana.Plugin({ require: ['kibana', 'elasticsearch', 'xpack_main', 'tile_map', 'task_manager'], - id: 'maps', + id: APP_ID, configPrefix: 'xpack.maps', publicDir: resolve(__dirname, 'public'), uiExports: { app: { - title: 'Maps', - description: 'Map application', + title: getAppTitle(), + description: i18n.translate('xpack.maps.appDescription', { + defaultMessage: 'Map application' + }), main: 'plugins/maps/index', icon: 'plugins/maps/icon.svg', - euiIconType: 'gisApp', + euiIconType: APP_ICON, }, injectDefaultVars(server) { const serverConfig = server.config(); @@ -86,9 +91,9 @@ export function maps(kibana) { .feature(this.id) .registerLicenseCheckResultsGenerator(checkLicense); - server.addSavedObjectsToSampleDataset('ecommerce', ecommerceSavedObjects); - server.addSavedObjectsToSampleDataset('flights', fligthsSavedObjects); - server.addSavedObjectsToSampleDataset('logs', webLogsSavedObjects); + server.addSavedObjectsToSampleDataset('ecommerce', getEcommerceSavedObjects()); + server.addSavedObjectsToSampleDataset('flights', getFlightsSavedObjects()); + server.addSavedObjectsToSampleDataset('logs', getWebLogsSavedObjects()); server.injectUiAppVars('maps', async () => { return await server.getInjectedUiAppVars('kibana'); }); diff --git a/x-pack/plugins/maps/public/angular/map_controller.js b/x-pack/plugins/maps/public/angular/map_controller.js index 5c941cc987099..c5a7f598ebcc4 100644 --- a/x-pack/plugins/maps/public/angular/map_controller.js +++ b/x-pack/plugins/maps/public/angular/map_controller.js @@ -6,7 +6,8 @@ import chrome from 'ui/chrome'; import React from 'react'; -import { I18nContext } from 'ui/i18n'; +import { I18nProvider } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { render, unmountComponentAtNode } from 'react-dom'; import { uiModules } from 'ui/modules'; import { timefilter } from 'ui/timefilter'; @@ -143,9 +144,9 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage const root = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID); render( - + - + , root ); @@ -201,7 +202,9 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage // TODO subscribe to store change and change when store updates title chrome.breadcrumbs.set([ - { text: 'Maps', href: '#' }, + { text: i18n.translate('xpack.maps.mapController.mapsBreadcrumbLabel', { + defaultMessage: 'Maps' + }), href: '#' }, { text: $scope.map.title } ]); @@ -216,7 +219,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage docTitle.change(savedMap.title); } catch(err) { toastNotifications.addDanger({ - title: `Error on saving '${savedMap.title}'`, + title: i18n.translate('xpack.maps.mapController.saveErrorMessage', { + defaultMessage: `Error on saving '{title}'`, + values: { title: savedMap.title } + }), text: err.message, 'data-test-subj': 'saveMapError', }); @@ -225,7 +231,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage if (id) { toastNotifications.addSuccess({ - title: `Saved '${savedMap.title}'`, + title: i18n.translate('xpack.maps.mapController.saveSuccessMessage', { + defaultMessage: `Saved '{title}'`, + values: { title: savedMap.title } + }), 'data-test-subj': 'saveMapSuccess', }); @@ -243,23 +252,35 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage timefilter.disableAutoRefreshSelector(); $scope.showDatePicker = true; // used by query-bar directive to enable timepikcer in query bar $scope.topNavMenu = [{ - key: 'full screen', - description: 'full screen', + key: i18n.translate('xpack.maps.mapController.fullScreenButtonLabel', { + defaultMessage: `full screen` + }), + description: i18n.translate('xpack.maps.mapController.fullScreenDescription', { + defaultMessage: `full screen` + }), testId: 'mapsFullScreenMode', run() { store.dispatch(enableFullScreen()); } }, { - key: 'inspect', - description: 'Open Inspector', + key: i18n.translate('xpack.maps.mapController.openInspectorButtonLabel', { + defaultMessage: `inspect` + }), + description: i18n.translate('xpack.maps.mapController.openInspectorDescription', { + defaultMessage: `Open Inspector` + }), testId: 'openInspectorButton', run() { const inspectorAdapters = getInspectorAdapters(store.getState()); Inspector.open(inspectorAdapters, {}); } }, { - key: 'save', - description: 'Save map', + key: i18n.translate('xpack.maps.mapController.saveMapButtonLabel', { + defaultMessage: `save` + }), + description: i18n.translate('xpack.maps.mapController.saveMapDescription', { + defaultMessage: `Save map` + }), testId: 'mapSaveButton', run: async () => { const onSave = ({ newTitle, newCopyOnSave, isTitleDuplicateConfirmed, onTitleDuplicate }) => { diff --git a/x-pack/plugins/maps/public/components/layer_addpanel/view.js b/x-pack/plugins/maps/public/components/layer_addpanel/view.js index 62568b6a88e46..0808d4ab31804 100644 --- a/x-pack/plugins/maps/public/components/layer_addpanel/view.js +++ b/x-pack/plugins/maps/public/components/layer_addpanel/view.js @@ -20,6 +20,7 @@ import { EuiFlyoutBody, EuiFlyoutFooter, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; export class AddLayerPanel extends Component { @@ -71,7 +72,10 @@ export class AddLayerPanel extends Component { }} fill > - Add layer + ); } @@ -102,7 +106,12 @@ export class AddLayerPanel extends Component { return ( -

Choose data source

+

+ +

{this._renderSourceCards()}
@@ -130,7 +139,10 @@ export class AddLayerPanel extends Component { onClick={this._clearSource} iconType="arrowLeft" > - Change data source + @@ -156,7 +168,12 @@ export class AddLayerPanel extends Component { > -

Add layer

+

+ +

@@ -175,7 +192,10 @@ export class AddLayerPanel extends Component { flush="left" data-test-subj="layerAddCancelButton" > - Cancel + diff --git a/x-pack/plugins/maps/public/components/layer_panel/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/components/layer_panel/__snapshots__/view.test.js.snap index be667afaf2da5..52eec2aea9dc6 100644 --- a/x-pack/plugins/maps/public/components/layer_panel/__snapshots__/view.test.js.snap +++ b/x-pack/plugins/maps/public/components/layer_panel/__snapshots__/view.test.js.snap @@ -35,7 +35,11 @@ exports[`LayerPanel is rendered 1`] = ` onClick={[Function]} type="button" > - Fit +
{ const removeBtn = ( @@ -23,11 +25,24 @@ export const FlyoutFooter = ({ cancelLayerPanel, saveLayerEdits, removeLayer, flush="right" data-test-subj="mapRemoveLayerButton" > - Remove layer + ); + + const cancelButtonLabel = hasStateChanged ? () : (); + + return ( @@ -35,7 +50,7 @@ export const FlyoutFooter = ({ cancelLayerPanel, saveLayerEdits, removeLayer, onClick={cancelLayerPanel} flush="left" > - {hasStateChanged ? 'Cancel' : 'Close'} + {cancelButtonLabel} @@ -49,7 +64,10 @@ export const FlyoutFooter = ({ cancelLayerPanel, saveLayerEdits, removeLayer, onClick={saveLayerEdits} fill > - Save & close + diff --git a/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join.js b/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join.js index 794897c7edd8a..d487fb3030c17 100644 --- a/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join.js +++ b/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join.js @@ -6,13 +6,12 @@ import _ from 'lodash'; import React, { Component } from 'react'; - import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon, } from '@elastic/eui'; - +import { i18n } from '@kbn/i18n'; import { JoinExpression } from './join_expression'; import { MetricsExpression } from './metrics_expression'; @@ -74,7 +73,10 @@ export class Join extends Component { } catch (err) { if (this._isMounted) { this.setState({ - loadError: `Unable to find Index pattern ${indexPatternId}` + loadError: i18n.translate('xpack.maps.layerPanel.join.noIndexPatternErrorMessage', { + defaultMessage: `Unable to find Index pattern {indexPatternId}`, + values: { indexPatternId } + }) }); } return; @@ -204,8 +206,12 @@ export class Join extends Component { className="mapJoinItem__delete" iconType="trash" color="danger" - aria-label="Delete join" - title="Delete join" + aria-label={i18n.translate('xpack.maps.layerPanel.join.deleteJoinAriaLabel', { + defaultMessage: 'Delete join' + })} + title={i18n.translate('xpack.maps.layerPanel.join.deleteJoinTitle', { + defaultMessage: 'Delete join' + })} onClick={onRemove} /> diff --git a/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join_expression.js b/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join_expression.js index 836efa89fb532..8950a67249a3d 100644 --- a/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join_expression.js +++ b/x-pack/plugins/maps/public/components/layer_panel/join_editor/resources/join_expression.js @@ -7,7 +7,6 @@ import _ from 'lodash'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; - import { EuiPopover, EuiPopoverTitle, @@ -15,9 +14,10 @@ import { EuiFormRow, EuiComboBox, } from '@elastic/eui'; - +import { i18n } from '@kbn/i18n'; import { IndexPatternSelect } from 'ui/index_patterns/components/index_pattern_select'; import { SingleFieldSelect } from '../../../../shared/components/single_field_select'; +import { FormattedMessage } from '@kbn/i18n/react'; import { indexPatternService, @@ -87,10 +87,14 @@ export class JoinExpression extends Component { return (
- Join + + + { if (!this.props.rightFields) { return ( - JOIN must be set + + + ); } @@ -69,7 +73,12 @@ export class MetricsExpression extends Component { return `${type} ${field}`; }); - + const useMetricDescription = i18n.translate('xpack.maps.layerPanel.metricsExpression.useMetricsDescription', { + defaultMessage: '{metricsLength, plural, one {and use metric} other {and use metrics}}', + values: { + metricsLength: metricExpressions.length + } + }); return ( 1 ? 'and use metrics' : 'and use metric'} + description={useMetricDescription} uppercase={false} value={metricExpressions.length > 0 ? metricExpressions.join(', ') : 'count'} /> } >
- Metrics + + + {this._renderMetricsEditor()}
diff --git a/x-pack/plugins/maps/public/components/layer_panel/join_editor/view.js b/x-pack/plugins/maps/public/components/layer_panel/join_editor/view.js index d6334d93fa617..600e645f1462b 100644 --- a/x-pack/plugins/maps/public/components/layer_panel/join_editor/view.js +++ b/x-pack/plugins/maps/public/components/layer_panel/join_editor/view.js @@ -16,6 +16,8 @@ import { } from '@elastic/eui'; import { Join } from './resources/join'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; export function JoinEditor({ joins, layer, onChange }) { @@ -69,10 +71,30 @@ export function JoinEditor({ joins, layer, onChange }) {
-
Term joins
+ +
+ +
+
- + +
diff --git a/x-pack/plugins/maps/public/components/layer_panel/settings_panel/settings_panel.js b/x-pack/plugins/maps/public/components/layer_panel/settings_panel/settings_panel.js index 9aa4d99e9e6be..c203892e455c1 100644 --- a/x-pack/plugins/maps/public/components/layer_panel/settings_panel/settings_panel.js +++ b/x-pack/plugins/maps/public/components/layer_panel/settings_panel/settings_panel.js @@ -18,6 +18,8 @@ import { EuiCallOut, } from '@elastic/eui'; import { ValidatedRange } from '../../../shared/components/validated_range'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; export function SettingsPanel(props) { @@ -53,7 +55,11 @@ export function SettingsPanel(props) {

{props.layer.getErrors()} @@ -67,12 +73,18 @@ export function SettingsPanel(props) { const renderZoomSliders = () => { return ( { return ( { return (

-
Settings
+ +
+ +
+
diff --git a/x-pack/plugins/maps/public/components/layer_panel/view.js b/x-pack/plugins/maps/public/components/layer_panel/view.js index 2144731f28cf6..36e4ac6f484f6 100644 --- a/x-pack/plugins/maps/public/components/layer_panel/view.js +++ b/x-pack/plugins/maps/public/components/layer_panel/view.js @@ -10,7 +10,6 @@ import { StyleTabs } from './style_tabs'; import { JoinEditor } from './join_editor'; import { FlyoutFooter } from './flyout_footer'; import { SettingsPanel } from './settings_panel'; - import { EuiButtonIcon, EuiFlexItem, @@ -26,6 +25,9 @@ import { EuiLink, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + export class LayerPanel extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { @@ -41,7 +43,7 @@ export class LayerPanel extends React.Component { return null; } - state = {} + state = {}; componentDidMount() { this._isMounted = true; @@ -133,11 +135,19 @@ export class LayerPanel extends React.Component { - Fit + + @@ -150,7 +160,11 @@ export class LayerPanel extends React.Component {
diff --git a/x-pack/plugins/maps/public/components/layer_panel/view.test.js b/x-pack/plugins/maps/public/components/layer_panel/view.test.js index ab9cb7b9735fd..200345bf92b74 100644 --- a/x-pack/plugins/maps/public/components/layer_panel/view.test.js +++ b/x-pack/plugins/maps/public/components/layer_panel/view.test.js @@ -29,7 +29,7 @@ jest.mock('./settings_panel', () => ({ })); import React from 'react'; -import { shallow } from 'enzyme'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { LayerPanel } from './view'; @@ -53,7 +53,7 @@ const defaultProps = { describe('LayerPanel', () => { test('is rendered', async () => { - const component = shallow( + const component = shallowWithIntl( @@ -69,7 +69,7 @@ describe('LayerPanel', () => { }); test('should render empty panel when selectedLayer is null', async () => { - const component = shallow( + const component = shallowWithIntl( { ]; } }; - const component = shallow( + const component = shallowWithIntl( diff --git a/x-pack/plugins/maps/public/components/widget_overlay/layer_control/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/components/widget_overlay/layer_control/__snapshots__/view.test.js.snap index d36e9c5e81072..0b1d9f96eade8 100644 --- a/x-pack/plugins/maps/public/components/widget_overlay/layer_control/__snapshots__/view.test.js.snap +++ b/x-pack/plugins/maps/public/components/widget_overlay/layer_control/__snapshots__/view.test.js.snap @@ -30,7 +30,11 @@ exports[`LayerControl is rendered 1`] = ` textTransform="none" >

- Layers +

@@ -47,7 +51,11 @@ exports[`LayerControl is rendered 1`] = ` size="xs" type="button" > - Add layer + @@ -92,7 +100,11 @@ exports[`LayerControl props isReadOnly 1`] = ` textTransform="none" >

- Layers +

diff --git a/x-pack/plugins/maps/public/components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js b/x-pack/plugins/maps/public/components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js index 8b5fba602b0a9..5622a17fd39b8 100644 --- a/x-pack/plugins/maps/public/components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js +++ b/x-pack/plugins/maps/public/components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { TOCEntry } from './view'; @@ -30,7 +30,7 @@ const defaultProps = { describe('TOCEntry', () => { test('is rendered', async () => { - const component = shallow( + const component = shallowWithIntl( @@ -47,7 +47,7 @@ describe('TOCEntry', () => { describe('props', () => { test('isReadOnly', async () => { - const component = shallow( + const component = shallowWithIntl( - Add layer + ); @@ -42,7 +46,12 @@ export function LayerControl({ isReadOnly, showAddLayerWizard }) { > -

Layers

+

+ +

{addLayer} diff --git a/x-pack/plugins/maps/public/components/widget_overlay/layer_control/view.test.js b/x-pack/plugins/maps/public/components/widget_overlay/layer_control/view.test.js index c0b58f864c2ad..aba7733dd8ed6 100644 --- a/x-pack/plugins/maps/public/components/widget_overlay/layer_control/view.test.js +++ b/x-pack/plugins/maps/public/components/widget_overlay/layer_control/view.test.js @@ -11,7 +11,7 @@ jest.mock('./layer_toc', () => ({ })); import React from 'react'; -import { shallow } from 'enzyme'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { LayerControl } from './view'; @@ -21,7 +21,7 @@ const defaultProps = { describe('LayerControl', () => { test('is rendered', () => { - const component = shallow( + const component = shallowWithIntl( @@ -33,7 +33,7 @@ describe('LayerControl', () => { describe('props', () => { test('isReadOnly', () => { - const component = shallow( + const component = shallowWithIntl( { @@ -37,7 +38,10 @@ export function ViewControl({ isSetViewOpen, closeSetView, openSetView, mouseCoo onClick={toggleSetViewVisibility} data-test-subj="toggleSetViewVisibilityButton" > - Go to + )} isOpen={isSetViewOpen} closePopover={closeSetView} @@ -57,8 +61,18 @@ export function ViewControl({ isSetViewOpen, closeSetView, openSetView, mouseCoo

- lat: {lat},{' '} - lon: {lon} + + + {lat},{' '} + + + {lon}

diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js index cb9c4aef05c45..f926ceee1cd52 100644 --- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js @@ -5,6 +5,7 @@ */ import _ from 'lodash'; +import { i18n } from '@kbn/i18n'; /** * Converts Elasticsearch search results into GeoJson FeatureCollection @@ -27,7 +28,11 @@ export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType) { } else if (geoFieldType === 'geo_shape') { geometries = geoShapeToGeometry(properties[geoFieldName]); } else { - throw new Error(`Unsupported field type, expected: geo_shape or geo_point, you provided: ${geoFieldType}`); + const errorMessage = i18n.translate('xpack.maps.elasticsearch_geo_utils.unsupportedFieldTypeErrorMessage', { + defaultMessage: 'Unsupported field type, expected: geo_shape or geo_point, you provided: {geoFieldType}', + values: { geoFieldType } + }); + throw new Error(errorMessage); } // don't include geometry field value in properties @@ -63,8 +68,11 @@ export function geoPointToGeometry(value) { if (typeof value === 'string') { const commaSplit = value.split(','); if (commaSplit.length === 1) { - // Geo-point expressed as a geohash. - throw new Error(`Unable to convert to geojson, geohash not supported`); + const errorMessage = i18n.translate('xpack.maps.elasticsearch_geo_utils.geohashIsUnsupportedErrorMessage', { + defaultMessage: `Unable to convert to geojson, geohash not supported` + }); + + throw new Error(errorMessage); } // Geo-point expressed as a string with the format: "lat,lon". const lat = parseFloat(commaSplit[0]); @@ -78,7 +86,13 @@ export function geoPointToGeometry(value) { } if (!Array.isArray(value)) { - throw new Error(`Unsupported geo_point value: ${value}`); + const errorMessage = i18n.translate('xpack.maps.elasticsearch_geo_utils.unsupportedGeoPointValueErrorMessage', { + defaultMessage: `Unsupported geo_point value: {geoPointValue}`, + values: { + geoPointValue: value + } + }); + throw new Error(errorMessage); } if (value.length === 2 @@ -116,7 +130,10 @@ export function geoShapeToGeometry(value) { // TODO handle case where value is WKT and convert to geojson if (typeof value === 'string') { - throw new Error(`Unable to convert WKT to geojson, not supported`); + const errorMessage = i18n.translate('xpack.maps.elasticsearch_geo_utils.wktIsUnsupportedErrorMessage', { + defaultMessage: `Unable to convert WKT to geojson, not supported`, + }); + throw new Error(errorMessage); } const geoJson = _.cloneDeep(value); @@ -180,7 +197,11 @@ export function createExtentFilter(mapExtent, geoFieldName, geoFieldType) { } }; } else { - throw new Error(`Unsupported field type, expected: geo_shape or geo_point, you provided: ${geoFieldType}`); + const errorMessage = i18n.translate('xpack.maps.elasticsearch_geo_utils.unsupportedGeoFieldTypeErrorMessage', { + defaultMessage: `Unsupported field type, expected: geo_shape or geo_point, you provided: {geoFieldType}`, + values: { geoFieldType } + }); + throw new Error(errorMessage); } } diff --git a/x-pack/plugins/maps/public/inspector/views/map_details.js b/x-pack/plugins/maps/public/inspector/views/map_details.js index 038085043505a..389b69f005861 100644 --- a/x-pack/plugins/maps/public/inspector/views/map_details.js +++ b/x-pack/plugins/maps/public/inspector/views/map_details.js @@ -15,6 +15,9 @@ import { EuiTableRow, EuiTableRowCell, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + const DETAILS_TAB_ID = 'details'; const STYLE_TAB_ID = 'mapStyle'; @@ -23,11 +26,15 @@ class MapDetails extends Component { tabs = [{ id: DETAILS_TAB_ID, - name: 'Map details', + name: i18n.translate('xpack.maps.inspector.mapDetailsTitle', { + defaultMessage: 'Map details' + }), dataTestSubj: 'mapDetailsTab', }, { id: STYLE_TAB_ID, - name: 'Mapbox style', + name: i18n.translate('xpack.maps.inspector.mapboxStyleTitle', { + defaultMessage: 'Mapbox style' + }), dataTestSubj: 'mapboxStyleTab', }]; @@ -60,21 +67,30 @@ class MapDetails extends Component { - Center lon + {this.props.centerLon} - Center lat + {this.props.centerLat} - Zoom + {this.props.zoom} diff --git a/x-pack/plugins/maps/public/inspector/views/map_view.js b/x-pack/plugins/maps/public/inspector/views/map_view.js index a42c7bbddfff9..e1bdcbdf191b7 100644 --- a/x-pack/plugins/maps/public/inspector/views/map_view.js +++ b/x-pack/plugins/maps/public/inspector/views/map_view.js @@ -9,6 +9,7 @@ import PropTypes from 'prop-types'; import { InspectorView } from 'ui/inspector'; import { MapDetails } from './map_details'; +import { i18n } from '@kbn/i18n'; class MapViewComponent extends Component { @@ -54,9 +55,13 @@ MapViewComponent.propTypes = { }; const MapView = { - title: 'Map details', + title: i18n.translate('xpack.maps.inspector.mapDetailsViewTitle', { + defaultMessage: 'Map details' + }), order: 30, - help: `View the map state`, + help: i18n.translate('xpack.maps.inspector.mapDetailsViewHelpText', { + defaultMessage: 'View the map state' + }), shouldShow(adapters) { return Boolean(adapters.map); }, diff --git a/x-pack/plugins/maps/public/meta.js b/x-pack/plugins/maps/public/meta.js index 91a296f08cf76..cace04a9ae7bf 100644 --- a/x-pack/plugins/maps/public/meta.js +++ b/x-pack/plugins/maps/public/meta.js @@ -36,10 +36,10 @@ export async function getDataSources() { return loadingMetaPromise; } +/** + * Should only call this after verifying `isMetadataLoaded` equals true + */ export function getDataSourcesSync() { - if (!isLoaded) { - throw new Error('Metadata is not loaded yet. Use isMetadataLoaded first before calling this function.'); - } return meta; } diff --git a/x-pack/plugins/maps/public/register_feature.js b/x-pack/plugins/maps/public/register_feature.js index 067348d41afa5..e112ca23483ed 100644 --- a/x-pack/plugins/maps/public/register_feature.js +++ b/x-pack/plugins/maps/public/register_feature.js @@ -8,14 +8,18 @@ import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; - +import { i18n } from '@kbn/i18n'; +import { APP_ID, APP_ICON } from '../common/constants'; +import { getAppTitle } from '../common/i18n_getters'; FeatureCatalogueRegistryProvider.register(() => { return { - id: 'maps', - title: 'Maps', - description: 'Explore geospatial data from Elasticsearch and the Elastic Maps Service', - icon: 'gisApp', + id: APP_ID, + title: getAppTitle(), + description: i18n.translate('xpack.maps.feature.appDescription', { + defaultMessage: 'Explore geospatial data from Elasticsearch and the Elastic Maps Service' + }), + icon: APP_ICON, path: '/app/maps', showOnHomePage: true, category: FeatureCatalogueCategory.DATA diff --git a/x-pack/plugins/maps/public/shared/components/layer_toc_actions.js b/x-pack/plugins/maps/public/shared/components/layer_toc_actions.js index ae71f5db63469..158d51b6fcfbb 100644 --- a/x-pack/plugins/maps/public/shared/components/layer_toc_actions.js +++ b/x-pack/plugins/maps/public/shared/components/layer_toc_actions.js @@ -15,6 +15,7 @@ import { EuiToolTip, EuiIconTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; function flattenPanelTree(tree, array = []) { array.push(tree); @@ -103,7 +104,7 @@ export class LayerTocActions extends Component { if (layer.hasErrors()) { smallLegendIcon = ( {icon} @@ -135,10 +140,14 @@ export class LayerTocActions extends Component { const visibilityToggle = this._getVisbilityIcon(); const panelTree = { id: 0, - title: 'Layer actions', + title: i18n.translate('xpack.maps.layerTocActions.layerActionsTitle', { + defaultMessage: 'Layer actions', + }), items: [ { - name: 'Fit to data', + name: i18n.translate('xpack.maps.layerTocActions.fitToDataTitle', { + defaultMessage: 'Fit to data', + }), icon: ( ), 'data-test-subj': 'fitToBoundsButton', - toolTipContent: this.state.supportsFitToBounds ? null : 'Layer does not support fit to data', + toolTipContent: this.state.supportsFitToBounds ? null : i18n.translate('xpack.maps.layerTocActions.noFitSupportTooltip', { + defaultMessage: 'Layer does not support fit to data', + }), disabled: !this.state.supportsFitToBounds, onClick: () => { this._closePopover(); @@ -154,7 +165,11 @@ export class LayerTocActions extends Component { }, }, { - name: this.props.layer.isVisible() ? 'Hide layer' : 'Show layer', + name: this.props.layer.isVisible() ? i18n.translate('xpack.maps.layerTocActions.hideLayerTitle', { + defaultMessage: 'Hide layer', + }) : i18n.translate('xpack.maps.layerTocActions.showLayerTitle', { + defaultMessage: 'Show layer', + }), icon: visibilityToggle, 'data-test-subj': 'layerVisibilityToggleButton', onClick: () => { diff --git a/x-pack/plugins/maps/public/shared/components/layer_toc_actions.test.js b/x-pack/plugins/maps/public/shared/components/layer_toc_actions.test.js index 118f6b20f23a2..71a5f1cedd008 100644 --- a/x-pack/plugins/maps/public/shared/components/layer_toc_actions.test.js +++ b/x-pack/plugins/maps/public/shared/components/layer_toc_actions.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { LayerTocActions } from './layer_toc_actions'; @@ -41,7 +41,7 @@ describe('LayerTocActions', () => { }); test('is rendered', async () => { - const component = shallow( + const component = shallowWithIntl( @@ -58,7 +58,7 @@ describe('LayerTocActions', () => { test('should disable fit to data when supportsFitToBounds is false', async () => { supportsFitToBounds = false; - const component = shallow( + const component = shallowWithIntl( @@ -75,7 +75,7 @@ describe('LayerTocActions', () => { test('should display spinner when layer is loading', async () => { isLayerLoading = true; - const component = shallow( + const component = shallowWithIntl( @@ -92,7 +92,7 @@ describe('LayerTocActions', () => { test('should show warning when layer has errors', async () => { hasErrors = true; - const component = shallow( + const component = shallowWithIntl( @@ -109,7 +109,7 @@ describe('LayerTocActions', () => { test('should show visible toggle when layer is not visible', async () => { isVisible = false; - const component = shallow( + const component = shallowWithIntl( @@ -126,7 +126,7 @@ describe('LayerTocActions', () => { test('should provide feedback when layer is not visible because of current zoom level', async () => { showAtZoomLevel = false; - const component = shallow( + const component = shallowWithIntl( diff --git a/x-pack/plugins/maps/public/shared/components/map_listing.js b/x-pack/plugins/maps/public/shared/components/map_listing.js index 03b85dfd6752a..c3e6e42b577a1 100644 --- a/x-pack/plugins/maps/public/shared/components/map_listing.js +++ b/x-pack/plugins/maps/public/shared/components/map_listing.js @@ -25,6 +25,8 @@ import { EuiCallOut, EuiBetaBadge, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; export const EMPTY_FILTER = ''; @@ -86,7 +88,9 @@ export class MapListing extends React.Component { await this.props.delete(this.state.selectedIds); } catch (error) { toastNotifications.addDanger({ - title: `Unable to delete map(s)`, + title: i18n.translate('xpack.maps.mapListing.unableToDeleteToastTitle', { + defaultMessage: `Unable to delete map(s)` + }), text: `${error}`, }); } @@ -168,14 +172,25 @@ export class MapListing extends React.Component { return ( -

{`You can't recover deleted items.`}

+

+ +

); @@ -186,14 +201,32 @@ export class MapListing extends React.Component { return (

- You have {this.state.totalItems} items, - but your listingLimit setting prevents the table below from displaying more than {this.props.listingLimit}. - You can change this setting under Advanced Settings. + + + + + .

@@ -208,10 +241,14 @@ export class MapListing extends React.Component { } if (this.hasNoItems()) { - return `Looks like you don't have any maps. Click the create button to create one.`; + return i18n.translate('xpack.maps.mapListing.noItemsDescription', { + defaultMessage: `Looks like you don't have any maps. Click the create button to create one.` + }); } - return 'No items matched your search.'; + return i18n.translate('xpack.maps.mapListing.noMatchDescription', { + defaultMessage: 'No items matched your search.' + }); } renderSearchBar() { @@ -225,7 +262,10 @@ export class MapListing extends React.Component { data-test-subj="deleteSelectedItems" key="delete" > - Delete selected + ); @@ -236,8 +276,12 @@ export class MapListing extends React.Component { {deleteBtn} { @@ -256,7 +300,9 @@ export class MapListing extends React.Component { const tableColumns = [ { field: 'title', - name: 'Title', + name: i18n.translate('xpack.maps.mapListing.titleFieldTitle', { + defaultMessage: 'Title' + }), sortable: true, render: (field, record) => ( - Create map + ); @@ -335,7 +386,10 @@ export class MapListing extends React.Component {

- Maps +

@@ -343,7 +397,11 @@ export class MapListing extends React.Component { diff --git a/x-pack/plugins/maps/public/shared/components/metric_select.js b/x-pack/plugins/maps/public/shared/components/metric_select.js index dda659106a1f7..3be3b13969cac 100644 --- a/x-pack/plugins/maps/public/shared/components/metric_select.js +++ b/x-pack/plugins/maps/public/shared/components/metric_select.js @@ -6,15 +6,30 @@ import React from 'react'; import PropTypes from 'prop-types'; - +import { i18n } from '@kbn/i18n'; import { EuiComboBox } from '@elastic/eui'; const AGG_OPTIONS = [ - { label: 'Average', value: 'avg' }, - { label: 'Count', value: 'count' }, - { label: 'Max', value: 'max' }, - { label: 'Min', value: 'min' }, - { label: 'Sum', value: 'sum' }, + { label: i18n.translate('xpack.maps.metricSelect.averageDropDownOptionLabel', { + defaultMessage: 'Average' + }), + value: 'avg' }, + { label: i18n.translate('xpack.maps.metricSelect.countDropDownOptionLabel', { + defaultMessage: 'Count' + }), + value: 'count' }, + { label: i18n.translate('xpack.maps.metricSelect.maxDropDownOptionLabel', { + defaultMessage: 'Max' + }), + value: 'max' }, + { label: i18n.translate('xpack.maps.metricSelect.minDropDownOptionLabel', { + defaultMessage: 'Min' + }), + value: 'min' }, + { label: i18n.translate('xpack.maps.metricSelect.sumDropDownOptionLabel', { + defaultMessage: 'Sum' + }), + value: 'sum' }, ]; export const METRIC_AGGREGATION_VALUES = AGG_OPTIONS.map(({ value }) => { return value; }); @@ -34,7 +49,11 @@ export function MetricSelect({ value, onChange, metricsFilter }) { return ( @@ -120,8 +128,12 @@ export function MetricsEditor({ fields, metrics, onChange, allowMultipleMetrics, return (); } diff --git a/x-pack/plugins/maps/public/shared/components/no_index_pattern_callout.js b/x-pack/plugins/maps/public/shared/components/no_index_pattern_callout.js index 6465756387829..498a8e78b56b5 100644 --- a/x-pack/plugins/maps/public/shared/components/no_index_pattern_callout.js +++ b/x-pack/plugins/maps/public/shared/components/no_index_pattern_callout.js @@ -8,24 +8,45 @@ import chrome from 'ui/chrome'; import React from 'react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; export function NoIndexPatternCallout() { return (

- You’ll need to{' '} + - create an index pattern - {' '} - with geospatial fields. + + +

- Don’t have any geospatial data sets?{' '} + - Get started with some sample data sets. +

diff --git a/x-pack/plugins/maps/public/shared/components/validated_range.js b/x-pack/plugins/maps/public/shared/components/validated_range.js index 9447dc6a21a2f..7b8ead00c2e5a 100644 --- a/x-pack/plugins/maps/public/shared/components/validated_range.js +++ b/x-pack/plugins/maps/public/shared/components/validated_range.js @@ -7,6 +7,7 @@ import React, { Fragment } from 'react'; import { EuiRange, EuiFormErrorText } from '@elastic/eui'; +import { FormattedText } from '@kbn/i18n/react'; function isWithinRange(min, max, value) { if (value >= min && value <= max) { @@ -68,7 +69,11 @@ export class ValidatedRange extends React.Component { if (!this.state.isValid) { errorMessage = ( - {`Must be between ${min} and ${max}`} + ); } diff --git a/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js b/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js index 7ce0ea9ce39ef..639df80b00b28 100644 --- a/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js +++ b/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js @@ -20,15 +20,14 @@ export class HeatmapLayer extends AbstractLayer { static createDescriptor(options) { const heatmapLayerDescriptor = super.createDescriptor(options); heatmapLayerDescriptor.type = HeatmapLayer.type; - const defaultStyle = HeatmapStyle.createDescriptor('coarse'); - heatmapLayerDescriptor.style = defaultStyle; + heatmapLayerDescriptor.style = HeatmapStyle.createDescriptor(); return heatmapLayerDescriptor; } constructor({ layerDescriptor, source, style }) { super({ layerDescriptor, source, style }); if (!style) { - const defaultStyle = HeatmapStyle.createDescriptor('coarse'); + const defaultStyle = HeatmapStyle.createDescriptor(); this._style = new HeatmapStyle(defaultStyle); } } diff --git a/x-pack/plugins/maps/public/shared/layers/sources/ems_file_source/create_source_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/ems_file_source/create_source_editor.js index 312ec9539b32c..03409e09fbbb3 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/ems_file_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/ems_file_source/create_source_editor.js @@ -13,6 +13,7 @@ import { import { getEmsVectorFilesMeta } from '../../../../meta'; import { getEmsUnavailableMessage } from '../ems_unavailable_message'; +import { i18n } from '@kbn/i18n'; export class EMSFileCreateSourceEditor extends React.Component { @@ -63,11 +64,19 @@ export class EMSFileCreateSourceEditor extends React.Component { return ( source.id === this._descriptor.id)); if (!meta) { - throw new Error(`Unable to find EMS vector shapes for id: ${this._descriptor.id}`); + throw new Error(i18n.translate('xpack.maps.source.emsFile.unableToFindIdErrorMessage', { + defaultMessage: `Unable to find EMS vector shapes for id: {id}`, + values: { + id: this._descriptor.id + } + })); } return meta; } @@ -59,8 +70,16 @@ export class EMSFileSource extends AbstractVectorSource { async getImmutableProperties() { const emsLink = await emsServiceSettings.getEMSHotLink({ id: this._descriptor.id }); return [ - { label: 'Data source', value: EMSFileSource.title }, - { label: 'Layer', value: this._descriptor.id, link: emsLink } + { + label: getDataSourceLabel(), + value: EMSFileSource.title + }, + { + label: i18n.translate('xpack.maps.source.emsFile.layerLabel', { + defaultMessage: `Layer`, + }), + value: this._descriptor.id, + link: emsLink } ]; } diff --git a/x-pack/plugins/maps/public/shared/layers/sources/ems_tms_source/create_source_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/ems_tms_source/create_source_editor.js index ac5c2c5f3cfd1..cdc61b1defe7b 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/ems_tms_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/ems_tms_source/create_source_editor.js @@ -13,6 +13,7 @@ import { import { getEmsTMSServices } from '../../../../meta'; import { getEmsUnavailableMessage } from '../ems_unavailable_message'; +import { i18n } from '@kbn/i18n'; export class EMSTMSCreateSourceEditor extends React.Component { @@ -53,7 +54,9 @@ export class EMSTMSCreateSourceEditor extends React.Component { return ( + + + diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/es_geo_grid_source.js index 3ce3167edd2b0..d15a9df61ea5e 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/es_geo_grid_source.js @@ -21,6 +21,8 @@ import { UpdateSourceEditor } from './update_source_editor'; import { GRID_RESOLUTION } from '../../grid_resolution'; import { SOURCE_DATA_ID_ORIGIN, ES_GEO_GRID } from '../../../../../common/constants'; import { filterPropertiesForTooltip } from '../../util'; +import { i18n } from '@kbn/i18n'; +import { getDataSourceLabel } from '../../../../../common/i18n_getters'; const COUNT_PROP_LABEL = 'count'; const COUNT_PROP_NAME = 'doc_count'; @@ -51,8 +53,12 @@ const aggSchemas = new Schemas([ export class ESGeoGridSource extends AbstractESSource { static type = ES_GEO_GRID; - static title = 'Grid aggregation'; - static description = 'Geospatial data grouped in grids with metrics for each gridded cell'; + static title = i18n.translate('xpack.maps.source.esGridTitle', { + defaultMessage: 'Grid aggregation' + }); + static description = i18n.translate('xpack.maps.source.esGridDescription', { + defaultMessage: 'Geospatial data grouped in grids with metrics for each gridded cell' + }); static createDescriptor({ indexPatternId, geoField, requestType, resolution }) { return { @@ -102,10 +108,27 @@ export class ESGeoGridSource extends AbstractESSource { } return [ - { label: 'Data source', value: ESGeoGridSource.title }, - { label: 'Index pattern', value: indexPatternTitle }, - { label: 'Geospatial field', value: this._descriptor.geoField }, - { label: 'Show as', value: this._descriptor.requestType }, + { + label: getDataSourceLabel(), + value: ESGeoGridSource.title + }, + { + label: i18n.translate('xpack.maps.source.esGrid.indexPatternLabel', { + defaultMessage: 'Index pattern' + }), + value: indexPatternTitle }, + { + label: i18n.translate('xpack.maps.source.esGrid.geospatialFieldLabel', { + defaultMessage: 'Geospatial field' + }), + value: this._descriptor.geoField + }, + { + label: i18n.translate('xpack.maps.source.esGrid.showasFieldLabel', { + defaultMessage: 'Show as' + }), + value: this._descriptor.requestType + }, ]; } @@ -145,7 +168,12 @@ export class ESGeoGridSource extends AbstractESSource { return 4; } - throw new Error(`Grid resolution param not recognized: ${this._descriptor.resolution}`); + throw new Error(i18n.translate('xpack.maps.source.esGrid.resolutionParamErrorMessage', { + defaultMessage: `Grid resolution param not recognized: {resolution}`, + values: { + resolution: this._descriptor.resolution + } + })); } async getGeoJsonWithMeta(layerName, searchFilters) { @@ -172,7 +200,9 @@ export class ESGeoGridSource extends AbstractESSource { const searchSource = await this._makeSearchSource(searchFilters, 0); const aggConfigs = new AggConfigs(indexPattern, this._makeAggConfigs(searchFilters.geogridPrecision), aggSchemas.all); searchSource.setField('aggs', aggConfigs.toDsl()); - const esResponse = await this._runEsQuery(layerName, searchSource, 'Elasticsearch geohash_grid aggregation request'); + const esResponse = await this._runEsQuery(layerName, searchSource, i18n.translate('xpack.maps.source.esGrid.inspectorDescription', { + defaultMessage: 'Elasticsearch geo grid aggregation request' + })); const tabifiedResp = tabifyAggResponse(aggConfigs, esResponse); const { featureCollection } = convertToGeoJson({ diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/resolution_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/resolution_editor.js index 16ea2c9e9b7db..fded85cc2fd49 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/resolution_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/resolution_editor.js @@ -12,12 +12,29 @@ import { EuiFlexItem, EuiFormLabel } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; const OPTIONS = [ - { value: GRID_RESOLUTION.COARSE, inputDisplay: 'coarse' }, - { value: GRID_RESOLUTION.FINE, inputDisplay: 'fine' }, - { value: GRID_RESOLUTION.MOST_FINE, inputDisplay: 'finest' } + { + value: GRID_RESOLUTION.COARSE, + inputDisplay: i18n.translate('xpack.maps.source.esGrid.coarseDropdownOption', { + defaultMessage: 'coarse' + }) + }, + { value: GRID_RESOLUTION.FINE, + inputDisplay: i18n.translate('xpack.maps.source.esGrid.fineDropdownOption', { + defaultMessage: 'fine' + }) + + }, + { + value: GRID_RESOLUTION.MOST_FINE, + inputDisplay: i18n.translate('xpack.maps.source.esGrid.finestDropdownOption', { + defaultMessage: 'finest' + }) + } ]; export function ResolutionEditor({ resolution, onChange }) { @@ -26,7 +43,10 @@ export function ResolutionEditor({ resolution, onChange }) { - Grid resolution + diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/update_source_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/update_source_editor.js index 1293fc3f5db2d..a876216b2b7d6 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_geo_grid_source/update_source_editor.js @@ -10,6 +10,7 @@ import { RENDER_AS } from './render_as'; import { MetricsEditor } from '../../../components/metrics_editor'; import { indexPatternService } from '../../../../kibana_services'; import { ResolutionEditor } from './resolution_editor'; +import { i18n } from '@kbn/i18n'; export class UpdateSourceEditor extends Component { @@ -33,7 +34,12 @@ export class UpdateSourceEditor extends Component { } catch (err) { if (this._isMounted) { this.setState({ - loadError: `Unable to find Index pattern ${this.props.indexPatternId}` + loadError: i18n.translate('xpack.maps.source.esGrid.noIndexPatternErrorMessage', { + defaultMessage: `Unable to find Index pattern {id}`, + values: { + id: this.props.indexPatternId + } + }) }); } return; diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_join_source.js b/x-pack/plugins/maps/public/shared/layers/sources/es_join_source.js index ded6824ea4525..3af36a696da13 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_join_source.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_join_source.js @@ -14,6 +14,7 @@ import { } from '../../../kibana_services'; import { AggConfigs } from 'ui/vis/agg_configs'; import { timefilter } from 'ui/timefilter/timefilter'; +import { i18n } from '@kbn/i18n'; const TERMS_AGG_NAME = 'join'; @@ -62,6 +63,7 @@ export class ESJoinSource extends AbstractESSource { static renderEditor({}) { + //no need to localize. this editor is never rendered. return `
editor details
`; } @@ -123,7 +125,12 @@ export class ESJoinSource extends AbstractESSource { requestDesc: this.getJoinDescription(leftSourceName, leftFieldName), }); } catch (error) { - throw new Error(`Elasticsearch search request failed, error: ${error.message}`); + throw new Error(i18n.translate('xpack.maps.source.esJoin.errorMessage', { + defaultMessage: `Elasticsearch search request failed, error: {message}`, + values: { + message: error.message + } + })); } const metricPropertyNames = configStates @@ -154,10 +161,21 @@ export class ESJoinSource extends AbstractESSource { return metric.type !== 'count' ? `${metric.type} ${metric.field}` : 'count'; }); const joinStatement = []; - joinStatement.push(`Join ${leftSourceName}:${leftFieldName} with`); + joinStatement.push(i18n.translate('xpack.maps.source.esJoin.joinLeftDescription', { + defaultMessage: `Join {leftSourceName}:{leftFieldName} with`, + values: { leftSourceName, leftFieldName } + })); joinStatement.push(`${this._descriptor.indexPatternTitle}:${this._descriptor.term}`); - joinStatement.push(`for metrics ${metrics.join(',')}`); - return `Elasticsearch terms aggregation request for ${joinStatement.join(' ')}`; + joinStatement.push(i18n.translate('xpack.maps.source.esJoin.joinMetricsDescription', { + defaultMessage: `for metrics {metrics}`, + values: { metrics: metrics.join(',') } + })); + return i18n.translate('xpack.maps.source.esJoin.joinDescription', { + defaultMessage: `Elasticsearch terms aggregation request for {description}`, + values: { + description: joinStatement.join(' ') + } + }); } _makeAggConfigs() { @@ -191,6 +209,7 @@ export class ESJoinSource extends AbstractESSource { } async getDisplayName() { + //no need to localize. this is never rendered. return `es_table ${this._descriptor.indexPatternId}`; } } diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/create_source_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/create_source_editor.js index 7d385daf83e6b..d806fd95d316e 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/create_source_editor.js @@ -13,6 +13,7 @@ import { IndexPatternSelect } from 'ui/index_patterns/components/index_pattern_s import { SingleFieldSelect } from '../../../components/single_field_select'; import { indexPatternService } from '../../../../kibana_services'; import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout'; +import { i18n } from '@kbn/i18n'; function filterGeoField(field) { return ['geo_point', 'geo_shape'].includes(field.type); @@ -22,14 +23,14 @@ export class CreateSourceEditor extends Component { static propTypes = { onSelect: PropTypes.func.isRequired, - } + }; state = { isLoadingIndexPattern: false, indexPatternId: '', geoField: '', noGeoIndexPatternsExist: false, - } + }; componentWillUnmount() { this._isMounted = false; @@ -52,7 +53,7 @@ export class CreateSourceEditor extends Component { indexPattern: undefined, geoField: undefined, }, this.debouncedLoad.bind(null, indexPatternId)); - } + }; debouncedLoad = _.debounce(async (indexPatternId) => { if (!indexPatternId || indexPatternId.length === 0) { @@ -126,10 +127,14 @@ export class CreateSourceEditor extends Component { return ( diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/es_search_source.js b/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/es_search_source.js index c8b9b74a49a2c..91c323868c563 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/es_search_source.js @@ -13,14 +13,20 @@ import { hitsToGeoJson } from '../../../../elasticsearch_geo_utils'; import { CreateSourceEditor } from './create_source_editor'; import { UpdateSourceEditor } from './update_source_editor'; import { ES_SEARCH } from '../../../../../common/constants'; +import { i18n } from '@kbn/i18n'; +import { getDataSourceLabel } from '../../../../../common/i18n_getters'; const DEFAULT_LIMIT = 2048; export class ESSearchSource extends AbstractESSource { static type = ES_SEARCH; - static title = 'Documents'; - static description = 'Geospatial data from a Kibana index pattern'; + static title = i18n.translate('xpack.maps.source.esSearchTitle', { + defaultMessage: 'Documents' + }); + static description = i18n.translate('xpack.maps.source.esSearchDescription', { + defaultMessage: 'Geospatial data from a Kibana index pattern' + }); static renderEditor({ onPreviewSource, inspectorAdapters }) { const onSelect = (sourceConfig) => { @@ -96,10 +102,28 @@ export class ESSearchSource extends AbstractESSource { } return [ - { label: 'Data source', value: ESSearchSource.title }, - { label: 'Index pattern', value: indexPatternTitle }, - { label: 'Geospatial field', value: this._descriptor.geoField }, - { label: 'Geospatial field type', value: geoFieldType }, + { + label: getDataSourceLabel(), + value: ESSearchSource.title + }, + { + label: i18n.translate('xpack.maps.source.esSearch.indexPatternLabel', { + defaultMessage: `Index pattern`, + }), + value: indexPatternTitle + }, + { + label: i18n.translate('xpack.maps.source.esSearch.geoFieldLabel', { + defaultMessage: 'Geospatial field', + }), + value: this._descriptor.geoField + }, + { + label: i18n.translate('xpack.maps.source.esSearch.geoFieldTypeLabel', { + defaultMessage: 'Geospatial field type', + }), + value: geoFieldType + }, ]; } diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/update_source_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/update_source_editor.js index 7aec6c1714f03..596ae7b763ea7 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_search_source/update_source_editor.js @@ -13,6 +13,7 @@ import { import { MultiFieldSelect } from '../../../components/multi_field_select'; import { indexPatternService } from '../../../../kibana_services'; +import { i18n } from '@kbn/i18n'; export class UpdateSourceEditor extends Component { @@ -43,7 +44,12 @@ export class UpdateSourceEditor extends Component { } catch (err) { if (this._isMounted) { this.setState({ - loadError: `Unable to find Index pattern ${this.props.indexPatternId}` + loadError: i18n.translate('xpack.maps.source.esSearch.loadErrorMessage', { + defaultMessage: `Unable to find Index pattern {id}`, + values: { + id: this.props.indexPatternId + } + }) }); } return; @@ -68,10 +74,17 @@ export class UpdateSourceEditor extends Component { return ( diff --git a/x-pack/plugins/maps/public/shared/layers/sources/es_source.js b/x-pack/plugins/maps/public/shared/layers/sources/es_source.js index d31d668341d15..24a6fc932f4e8 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/es_source.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/es_source.js @@ -14,6 +14,7 @@ import { createExtentFilter } from '../../../elasticsearch_geo_utils'; import { timefilter } from 'ui/timefilter/timefilter'; import _ from 'lodash'; import { AggConfigs } from 'ui/vis/agg_configs'; +import { i18n } from '@kbn/i18n'; export class AbstractESSource extends AbstractVectorSource { @@ -87,7 +88,10 @@ export class AbstractESSource extends AbstractVectorSource { requestDesc: requestDescription }); } catch(error) { - throw new Error(`Elasticsearch search request failed, error: ${error.message}`); + throw new Error('xpack.maps.source.esSource.requestFailedErrorMessage', { + defaultMessage: `Elasticsearch search request failed, error: {message}`, + values: { message: error.message } + }); } } @@ -174,7 +178,10 @@ export class AbstractESSource extends AbstractVectorSource { this.indexPattern = await indexPatternService.get(this._descriptor.indexPatternId); return this.indexPattern; } catch (error) { - throw new Error(`Unable to find Index pattern for id: ${this._descriptor.indexPatternId}`); + throw new Error(i18n.translate('xpack.maps.source.esSource.noIndexPatternErrorMessage', { + defaultMessage: `Unable to find Index pattern for id: {indexPatternId}`, + values: { indexPatternId: this._descriptor.indexPatternId } + })); } } @@ -194,7 +201,10 @@ export class AbstractESSource extends AbstractVectorSource { const indexPattern = await this._getIndexPattern(); const geoField = indexPattern.fields.byName[this._descriptor.geoField]; if (!geoField) { - throw new Error(`Index pattern ${indexPattern.title} no longer contains the geo field ${this._descriptor.geoField}`); + throw new Error(i18n.translate('xpack.maps.source.esSource.noGeoFieldErrorMessage', { + defaultMessage: `Index pattern {indexPatternTitle} no longer contains the geo field {geoField}`, + values: { indexPatternTitle: indexPattern.title, geoField: this._descriptor.geoField } + })); } return geoField; } diff --git a/x-pack/plugins/maps/public/shared/layers/sources/kibana_regionmap_source/create_source_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/kibana_regionmap_source/create_source_editor.js index c740df0129ad6..c7b7fbf71bb7f 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/kibana_regionmap_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/kibana_regionmap_source/create_source_editor.js @@ -11,10 +11,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { getKibanaRegionList } from '../../../../meta'; - -const NO_REGIONMAP_LAYERS_MSG = - 'No vector layers are available.' + - ' Ask your system administrator to set "map.regionmap" in kibana.yml.'; +import { i18n } from '@kbn/i18n'; export class CreateSourceEditor extends React.Component { @@ -42,8 +39,6 @@ export class CreateSourceEditor extends React.Component { render() { - - const onChange = ({ target }) => { const selectedName = target.options[target.selectedIndex].text; this.props.onSelect({ name: selectedName }); @@ -58,8 +53,15 @@ export class CreateSourceEditor extends React.Component { return ( source.name === this._descriptor.name); if (!meta) { - throw new Error(`Unable to find map.regionmap configuration for ${this._descriptor.name}`); + throw new Error(i18n.translate('xpack.maps.source.kbnRegionMap.noConfigErrorMessage', { + defaultMessage: `Unable to find map.regionmap configuration for {name}`, + values: { + name: this._descriptor.name + } + }) + ); } return meta; } diff --git a/x-pack/plugins/maps/public/shared/layers/sources/kibana_tilemap_source/create_source_editor.js b/x-pack/plugins/maps/public/shared/layers/sources/kibana_tilemap_source/create_source_editor.js index b5b2b6130ac2c..858bec1b855e1 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/kibana_tilemap_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/kibana_tilemap_source/create_source_editor.js @@ -12,12 +12,7 @@ import { } from '@elastic/eui'; import { getKibanaTileMap } from '../../../../meta'; - -const NO_TILEMAP_LAYER_MSG = - 'No tilemap layer is available.' + - ' Ask your system administrator to set "map.tilemap.url" in kibana.yml.'; - - +import { i18n } from '@kbn/i18n'; export class CreateSourceEditor extends Component { @@ -56,8 +51,15 @@ export class CreateSourceEditor extends Component { return ( this._handleServiceUrlChange(e)} /> - + this._handleLayersChange(e)} /> - + this._handleStylesChange(e)} /> diff --git a/x-pack/plugins/maps/public/shared/layers/sources/xyz_tms_source.js b/x-pack/plugins/maps/public/shared/layers/sources/xyz_tms_source.js index ed78374727b82..dac1d4b7aeba6 100644 --- a/x-pack/plugins/maps/public/shared/layers/sources/xyz_tms_source.js +++ b/x-pack/plugins/maps/public/shared/layers/sources/xyz_tms_source.js @@ -13,12 +13,18 @@ import { import { AbstractTMSSource } from './tms_source'; import { TileLayer } from '../tile_layer'; +import { i18n } from '@kbn/i18n'; +import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters'; export class XYZTMSSource extends AbstractTMSSource { static type = 'EMS_XYZ'; - static title = 'Tile Map Service from URL'; - static description = 'Map tiles from a URL that includes the XYZ coordinates'; + static title = i18n.translate('xpack.maps.source.ems_xyzTitle', { + defaultMessage: 'Tile Map Service from URL' + }); + static description = i18n.translate('xpack.maps.source.ems_xyzDescription', { + defaultMessage: 'Map tiles from a URL that includes the XYZ coordinates' + }); static icon = 'grid'; static createDescriptor(urlTemplate) { @@ -39,8 +45,8 @@ export class XYZTMSSource extends AbstractTMSSource { async getImmutableProperties() { return [ - { label: 'Data source', value: XYZTMSSource.title }, - { label: 'Url', value: this._descriptor.urlTemplate }, + { label: getDataSourceLabel(), value: XYZTMSSource.title }, + { label: getUrlLabel(), value: this._descriptor.urlTemplate }, ]; } @@ -92,7 +98,7 @@ class XYZTMSEditor extends React.Component { return ( this._handleTMSInputChange(e)} /> diff --git a/x-pack/plugins/maps/public/shared/layers/styles/components/static_dynamic_style_row.js b/x-pack/plugins/maps/public/shared/layers/styles/components/static_dynamic_style_row.js index d1e8ac6b8e26a..f4ef285e94b9c 100644 --- a/x-pack/plugins/maps/public/shared/layers/styles/components/static_dynamic_style_row.js +++ b/x-pack/plugins/maps/public/shared/layers/styles/components/static_dynamic_style_row.js @@ -7,6 +7,7 @@ import React from 'react'; import { VectorStyle } from '../vector_style'; import _ from 'lodash'; +import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, @@ -93,7 +94,13 @@ export class StaticDynamicStyleRow extends React.Component { render() { const isDynamic = this._isDynamic(); const dynamicTooltipContent = - isDynamic ? 'Use static styling properties to symbolize features.' : 'Use property values to symbolize features.'; + isDynamic ? + i18n.translate('xpack.maps.styles.staticDynamic.staticDescription', { + defaultMessage: 'Use static styling properties to symbolize features.' + }) : + i18n.translate('xpack.maps.styles.staticDynamic.dynamicDescription', { + defaultMessage: 'Use property values to symbolize features.' + }); return ( diff --git a/x-pack/plugins/maps/public/shared/layers/styles/components/vector/field_select.js b/x-pack/plugins/maps/public/shared/layers/styles/components/vector/field_select.js index 5163a2fe46d23..47ee4277236bd 100644 --- a/x-pack/plugins/maps/public/shared/layers/styles/components/vector/field_select.js +++ b/x-pack/plugins/maps/public/shared/layers/styles/components/vector/field_select.js @@ -9,6 +9,7 @@ import React from 'react'; import { EuiComboBox } from '@elastic/eui'; import { SOURCE_DATA_ID_ORIGIN } from '../../../../../../common/constants'; +import { i18n } from '@kbn/i18n'; export function FieldSelect({ fields, selectedField, onChange }) { @@ -64,7 +65,11 @@ export function FieldSelect({ fields, selectedField, onChange }) { singleSelection={{ asPlainText: true }} isClearable={false} fullWidth - placeholder="Select a field" + placeholder={ + i18n.translate('xpack.maps.styles.vector.selectFieldPlaceholder', { + defaultMessage: 'Select a field' + }) + } /> ); } diff --git a/x-pack/plugins/maps/public/shared/layers/styles/components/vector/size/size_range_selector.js b/x-pack/plugins/maps/public/shared/layers/styles/components/vector/size/size_range_selector.js index 88b5926e2cf36..0ebb46495308f 100644 --- a/x-pack/plugins/maps/public/shared/layers/styles/components/vector/size/size_range_selector.js +++ b/x-pack/plugins/maps/public/shared/layers/styles/components/vector/size/size_range_selector.js @@ -12,6 +12,7 @@ import { EuiFlexItem, } from '@elastic/eui'; import { ValidatedRange } from '../../../../../components/validated_range'; +import { i18n } from '@kbn/i18n'; import { DEFAULT_MIN_SIZE, DEFAULT_MAX_SIZE } from '../../../vector_style_defaults'; export function SizeRangeSelector({ minSize, maxSize, onChange }) { @@ -35,7 +36,11 @@ export function SizeRangeSelector({ minSize, maxSize, onChange }) { { + return [ + { + 'id': '2c9c1f60-1909-11e9-919b-ffe5949a18d2', + 'type': 'map', + 'updated_at': '2019-01-15T21:12:56.253Z', + 'version': 5, + 'references': [ + { + 'name': 'layer_1_join_0_index_pattern', + 'type': 'index-pattern', + 'id': 'ff959d40-b880-11e8-a6d9-e546fe2bba5f' + }, + { + 'name': 'layer_2_join_0_index_pattern', + 'type': 'index-pattern', + 'id': 'ff959d40-b880-11e8-a6d9-e546fe2bba5f' + }, + { + 'name': 'layer_3_join_0_index_pattern', + 'type': 'index-pattern', + 'id': 'ff959d40-b880-11e8-a6d9-e546fe2bba5f' + }, + { + 'name': 'layer_4_join_0_index_pattern', + 'type': 'index-pattern', + 'id': 'ff959d40-b880-11e8-a6d9-e546fe2bba5f' + }, + { + 'name': 'layer_5_source_index_pattern', + 'type': 'index-pattern', + 'id': 'ff959d40-b880-11e8-a6d9-e546fe2bba5f' + }, + { + 'name': 'layer_6_source_index_pattern', + 'type': 'index-pattern', + 'id': 'ff959d40-b880-11e8-a6d9-e546fe2bba5f' + } + ], + 'migrationVersion': { + 'map': '7.1.0' + }, + 'attributes': { + 'title': i18n.translate('xpack.maps.sampleData.ecommerceSpec.mapsTitle', { + defaultMessage: '[eCommerce] Orders by Country', + }), + 'description': '', + 'mapStateJSON': '{"zoom":2.11,"center":{"lon":-15.07605,"lat":45.88578},"timeFilters":{"from":"now-7d","to":"now"},"refreshConfig":{"isPaused":true,"interval":0},"query":{"query":"","language":"kuery"}}', + 'layerListJSON': '[{"id":"0hmz5","alpha":1,"sourceDescriptor":{"type":"EMS_TMS","id":"road_map"},"visible":true,"style":{"type":"TILE","properties":{}},"type":"TILE","minZoom":0,"maxZoom":24},{"id":"7ameq","label":null,"minZoom":0,"maxZoom":24,"alpha":1,"sourceDescriptor":{"type":"EMS_FILE","id":"world_countries"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"count of kibana_sample_data_ecommerce:geoip.country_iso_code","name":"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.country_iso_code","origin":"join"},"color":"Green to Red"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"STATIC","options":{"size":10}}}},"type":"VECTOR","joins":[{"leftField":"iso2","right":{"id":"741db9c6-8ebb-4ea9-9885-b6b4ac019d14","indexPatternTitle":"kibana_sample_data_ecommerce","term":"geoip.country_iso_code","indexPatternRefName":"layer_1_join_0_index_pattern"}}]},{"id":"jmtgf","label":"United States","minZoom":0,"maxZoom":24,"alpha":1,"sourceDescriptor":{"type":"EMS_FILE","id":"usa_states"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"count of kibana_sample_data_ecommerce:geoip.region_name","name":"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name","origin":"join"},"color":"Blues"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"STATIC","options":{"size":10}}}},"type":"VECTOR","joins":[{"leftField":"name","right":{"id":"30a0ec24-49b6-476a-b4ed-6c1636333695","indexPatternTitle":"kibana_sample_data_ecommerce","term":"geoip.region_name","indexPatternRefName":"layer_2_join_0_index_pattern"}}]},{"id":"ui5f8","label":"France","minZoom":0,"maxZoom":24,"alpha":1,"sourceDescriptor":{"type":"EMS_FILE","id":"france_departments"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"count of kibana_sample_data_ecommerce:geoip.region_name","name":"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name","origin":"join"},"color":"Blues"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"STATIC","options":{"size":10}}}},"type":"VECTOR","joins":[{"leftField":"label_en","right":{"id":"e325c9da-73fa-4b3b-8b59-364b99370826","indexPatternTitle":"kibana_sample_data_ecommerce","term":"geoip.region_name","indexPatternRefName":"layer_3_join_0_index_pattern"}}]},{"id":"y3fjb","label":"United Kingdom","minZoom":0,"maxZoom":24,"alpha":1,"sourceDescriptor":{"type":"EMS_FILE","id":"uk_subdivisions"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"count of kibana_sample_data_ecommerce:geoip.region_name","name":"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name","origin":"join"},"color":"Blues"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"STATIC","options":{"size":10}}}},"type":"VECTOR","joins":[{"leftField":"label_en","right":{"id":"612d805d-8533-43a9-ac0e-cbf51fe63dcd","indexPatternTitle":"kibana_sample_data_ecommerce","term":"geoip.region_name","indexPatternRefName":"layer_4_join_0_index_pattern"}}]},{"id":"c54wk","label":"Sales","minZoom":9,"maxZoom":24,"alpha":1,"sourceDescriptor":{"id":"04c983b0-8cfa-4e6a-a64b-52c10b7008fe","type":"ES_SEARCH","geoField":"geoip.location","limit":2048,"filterByMapBounds":true,"tooltipProperties":["category","customer_gender","manufacturer","order_id","total_quantity","total_unique_products","taxful_total_price","order_date","geoip.region_name","geoip.country_iso_code"],"indexPatternRefName":"layer_5_source_index_pattern"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"taxful_total_price","name":"taxful_total_price","origin":"source"},"color":"Greens"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"STATIC","options":{"size":10}}}},"type":"VECTOR"},{"id":"qvhh3","label":"Total Sales Revenue","minZoom":0,"maxZoom":9,"alpha":1,"sourceDescriptor":{"type":"ES_GEO_GRID","resolution":"COARSE","id":"aa7f87b8-9dc5-42be-b19e-1a2fa09b6cad","geoField":"geoip.location","requestType":"point","metrics":[{"type":"count"},{"type":"sum","field":"taxful_total_price"}],"indexPatternRefName":"layer_6_source_index_pattern"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"Count","name":"doc_count","origin":"source"},"color":"Greens"}},"lineColor":{"type":"STATIC","options":{"color":"#cccccc"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"DYNAMIC","options":{"field":{"label":"sum of taxful_total_price","name":"sum_of_taxful_total_price","origin":"source"},"minSize":1,"maxSize":20}}}},"type":"VECTOR"}]', + 'uiStateJSON': '{"isDarkMode":false}', + 'bounds': { 'type': 'envelope', 'coordinates': [[-117.50707, 72.64116], [87.35497, -4.16541]] } + } + } + ]; +}; diff --git a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.json b/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.json deleted file mode 100644 index 60082162e605b..0000000000000 --- a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "id":"2c9c1f60-1909-11e9-919b-ffe5949a18d2", - "type":"map", - "updated_at":"2019-01-15T21:12:56.253Z", - "version":5, - "references" : [ - { - "name" : "layer_1_join_0_index_pattern", - "type" : "index-pattern", - "id" : "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - }, - { - "name" : "layer_2_join_0_index_pattern", - "type" : "index-pattern", - "id" : "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - }, - { - "name" : "layer_3_join_0_index_pattern", - "type" : "index-pattern", - "id" : "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - }, - { - "name" : "layer_4_join_0_index_pattern", - "type" : "index-pattern", - "id" : "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - }, - { - "name" : "layer_5_source_index_pattern", - "type" : "index-pattern", - "id" : "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - }, - { - "name" : "layer_6_source_index_pattern", - "type" : "index-pattern", - "id" : "ff959d40-b880-11e8-a6d9-e546fe2bba5f" - } - ], - "migrationVersion" : { - "map" : "7.1.0" - }, - "attributes":{ - "title":"[eCommerce] Orders by Country", - "description":"", - "mapStateJSON":"{\"zoom\":2.11,\"center\":{\"lon\":-15.07605,\"lat\":45.88578},\"timeFilters\":{\"from\":\"now-7d\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"}}", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"7ameq\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"world_countries\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"count of kibana_sample_data_ecommerce:geoip.country_iso_code\",\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.country_iso_code\",\"origin\":\"join\"},\"color\":\"Green to Red\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"iso2\",\"right\":{\"id\":\"741db9c6-8ebb-4ea9-9885-b6b4ac019d14\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.country_iso_code\",\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]},{\"id\":\"jmtgf\",\"label\":\"United States\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"usa_states\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"count of kibana_sample_data_ecommerce:geoip.region_name\",\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"30a0ec24-49b6-476a-b4ed-6c1636333695\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_2_join_0_index_pattern\"}}]},{\"id\":\"ui5f8\",\"label\":\"France\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"france_departments\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"count of kibana_sample_data_ecommerce:geoip.region_name\",\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"label_en\",\"right\":{\"id\":\"e325c9da-73fa-4b3b-8b59-364b99370826\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_3_join_0_index_pattern\"}}]},{\"id\":\"y3fjb\",\"label\":\"United Kingdom\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"uk_subdivisions\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"count of kibana_sample_data_ecommerce:geoip.region_name\",\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"label_en\",\"right\":{\"id\":\"612d805d-8533-43a9-ac0e-cbf51fe63dcd\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_4_join_0_index_pattern\"}}]},{\"id\":\"c54wk\",\"label\":\"Sales\",\"minZoom\":9,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"id\":\"04c983b0-8cfa-4e6a-a64b-52c10b7008fe\",\"type\":\"ES_SEARCH\",\"geoField\":\"geoip.location\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[\"category\",\"customer_gender\",\"manufacturer\",\"order_id\",\"total_quantity\",\"total_unique_products\",\"taxful_total_price\",\"order_date\",\"geoip.region_name\",\"geoip.country_iso_code\"],\"indexPatternRefName\":\"layer_5_source_index_pattern\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"taxful_total_price\",\"name\":\"taxful_total_price\",\"origin\":\"source\"},\"color\":\"Greens\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\"},{\"id\":\"qvhh3\",\"label\":\"Total Sales Revenue\",\"minZoom\":0,\"maxZoom\":9,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"resolution\":\"COARSE\",\"id\":\"aa7f87b8-9dc5-42be-b19e-1a2fa09b6cad\",\"geoField\":\"geoip.location\",\"requestType\":\"point\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"indexPatternRefName\":\"layer_6_source_index_pattern\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Greens\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"sum of taxful_total_price\",\"name\":\"sum_of_taxful_total_price\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":20}}}},\"type\":\"VECTOR\"}]", - "uiStateJSON":"{\"isDarkMode\":false}", - "bounds":{"type":"envelope","coordinates":[[-117.50707,72.64116],[87.35497,-4.16541]]} - } - } -] diff --git a/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js b/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js new file mode 100644 index 0000000000000..b05690bb0d5fe --- /dev/null +++ b/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js @@ -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. + */ +/* eslint-disable max-len */ + +import { i18n } from '@kbn/i18n'; + +export const getFlightsSavedObjects = () => { + return [ + { + 'id': '5dd88580-1906-11e9-919b-ffe5949a18d2', + 'type': 'map', + 'updated_at': '2019-01-15T20:44:54.767Z', + 'version': 2, + 'references': [ + { + 'name': 'layer_1_source_index_pattern', + 'type': 'index-pattern', + 'id': 'd3d7af60-4c81-11e8-b3d7-01146121b73d' + }, + { + 'name': 'layer_2_source_index_pattern', + 'type': 'index-pattern', + 'id': 'd3d7af60-4c81-11e8-b3d7-01146121b73d' + }, + { + 'name': 'layer_3_source_index_pattern', + 'type': 'index-pattern', + 'id': 'd3d7af60-4c81-11e8-b3d7-01146121b73d' + } + ], + 'migrationVersion': { + 'map': '7.1.0' + }, + 'attributes': { + 'title': i18n.translate('xpack.maps.sampleData.flightaSpec.mapsTitle', { + defaultMessage: '[Flights] Origin and Destination Flight Time' + }), + 'description': '', + 'mapStateJSON': '{"zoom":3.14,"center":{"lon":-89.58746,"lat":38.38637},"timeFilters":{"from":"now-7d","to":"now"},"refreshConfig":{"isPaused":true,"interval":0},"query":{"query":"","language":"kuery"}}', + 'layerListJSON': '[{"id":"0hmz5","alpha":1,"sourceDescriptor":{"type":"EMS_TMS","id":"road_map"},"visible":true,"style":{"type":"TILE","properties":{}},"type":"TILE","minZoom":0,"maxZoom":24},{"id":"jzppx","label":"Flights","minZoom":9,"maxZoom":24,"alpha":1,"sourceDescriptor":{"id":"040e0f25-9687-4569-a1e0-76f1a108da56","type":"ES_SEARCH","geoField":"DestLocation","limit":2048,"filterByMapBounds":true,"tooltipProperties":["Carrier","DestCityName","DestCountry","OriginCityName","OriginCountry","FlightDelayMin","FlightTimeMin","DistanceMiles","AvgTicketPrice","FlightDelay"],"indexPatternRefName":"layer_1_source_index_pattern"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"FlightTimeMin","name":"FlightTimeMin","origin":"source"},"color":"Greens"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"DYNAMIC","options":{"field":{"label":"DistanceMiles","name":"DistanceMiles","origin":"source"},"minSize":1,"maxSize":32}}}},"type":"VECTOR"},{"id":"y4jsz","label":"Flight Origin Location","minZoom":0,"maxZoom":9,"alpha":1,"sourceDescriptor":{"type":"ES_GEO_GRID","resolution":"COARSE","id":"fe893f84-388e-4865-8df4-650748533a77","geoField":"OriginLocation","requestType":"point","metrics":[{"type":"count"},{"type":"avg","field":"FlightTimeMin"}],"indexPatternRefName":"layer_2_source_index_pattern"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"Count","name":"doc_count","origin":"source"},"color":"Blues"}},"lineColor":{"type":"STATIC","options":{"color":"#110081"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"DYNAMIC","options":{"field":{"label":"avg of FlightTimeMin","name":"avg_of_FlightTimeMin","origin":"source"},"minSize":1,"maxSize":32}}}},"type":"VECTOR"},{"id":"x8xpo","label":"Flight Destination Location","minZoom":0,"maxZoom":9,"alpha":1,"sourceDescriptor":{"type":"ES_GEO_GRID","resolution":"COARSE","id":"60a7346a-8c5f-4c03-b7d1-e8b36e847551","geoField":"DestLocation","requestType":"point","metrics":[{"type":"count"},{"type":"avg","field":"FlightDelayMin"}],"indexPatternRefName":"layer_3_source_index_pattern"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"Count","name":"doc_count","origin":"source"},"color":"Reds"}},"lineColor":{"type":"STATIC","options":{"color":"#af0303"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"DYNAMIC","options":{"field":{"label":"avg of FlightDelayMin","name":"avg_of_FlightDelayMin","origin":"source"},"minSize":1,"maxSize":32}}}},"type":"VECTOR"}]', + 'uiStateJSON': '{"isDarkMode":false}', + 'bounds': { + 'type': 'envelope', 'coordinates': [[-139.83779, 56.64828], [-39.33713, 14.04811]] } + } + } + ]; +}; diff --git a/x-pack/plugins/maps/server/sample_data/flights_saved_objects.json b/x-pack/plugins/maps/server/sample_data/flights_saved_objects.json deleted file mode 100644 index 333a529b9d927..0000000000000 --- a/x-pack/plugins/maps/server/sample_data/flights_saved_objects.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "id":"5dd88580-1906-11e9-919b-ffe5949a18d2", - "type":"map", - "updated_at":"2019-01-15T20:44:54.767Z", - "version":2, - "references" : [ - { - "name" : "layer_1_source_index_pattern", - "type" : "index-pattern", - "id" : "d3d7af60-4c81-11e8-b3d7-01146121b73d" - }, - { - "name" : "layer_2_source_index_pattern", - "type" : "index-pattern", - "id" : "d3d7af60-4c81-11e8-b3d7-01146121b73d" - }, - { - "name" : "layer_3_source_index_pattern", - "type" : "index-pattern", - "id" : "d3d7af60-4c81-11e8-b3d7-01146121b73d" - } - ], - "migrationVersion" : { - "map" : "7.1.0" - }, - "attributes":{ - "title":"[Flights] Origin and Destination Flight Time", - "description":"", - "mapStateJSON":"{\"zoom\":3.14,\"center\":{\"lon\":-89.58746,\"lat\":38.38637},\"timeFilters\":{\"from\":\"now-7d\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"}}", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"jzppx\",\"label\":\"Flights\",\"minZoom\":9,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"id\":\"040e0f25-9687-4569-a1e0-76f1a108da56\",\"type\":\"ES_SEARCH\",\"geoField\":\"DestLocation\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[\"Carrier\",\"DestCityName\",\"DestCountry\",\"OriginCityName\",\"OriginCountry\",\"FlightDelayMin\",\"FlightTimeMin\",\"DistanceMiles\",\"AvgTicketPrice\",\"FlightDelay\"],\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"FlightTimeMin\",\"name\":\"FlightTimeMin\",\"origin\":\"source\"},\"color\":\"Greens\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"DistanceMiles\",\"name\":\"DistanceMiles\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":32}}}},\"type\":\"VECTOR\"},{\"id\":\"y4jsz\",\"label\":\"Flight Origin Location\",\"minZoom\":0,\"maxZoom\":9,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"resolution\":\"COARSE\",\"id\":\"fe893f84-388e-4865-8df4-650748533a77\",\"geoField\":\"OriginLocation\",\"requestType\":\"point\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"avg\",\"field\":\"FlightTimeMin\"}],\"indexPatternRefName\":\"layer_2_source_index_pattern\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#110081\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"avg of FlightTimeMin\",\"name\":\"avg_of_FlightTimeMin\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":32}}}},\"type\":\"VECTOR\"},{\"id\":\"x8xpo\",\"label\":\"Flight Destination Location\",\"minZoom\":0,\"maxZoom\":9,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"resolution\":\"COARSE\",\"id\":\"60a7346a-8c5f-4c03-b7d1-e8b36e847551\",\"geoField\":\"DestLocation\",\"requestType\":\"point\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"avg\",\"field\":\"FlightDelayMin\"}],\"indexPatternRefName\":\"layer_3_source_index_pattern\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Reds\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#af0303\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"avg of FlightDelayMin\",\"name\":\"avg_of_FlightDelayMin\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":32}}}},\"type\":\"VECTOR\"}]", - "uiStateJSON":"{\"isDarkMode\":false}", - "bounds":{"type":"envelope","coordinates":[[-139.83779,56.64828],[-39.33713,14.04811]]} - } - } -] diff --git a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js new file mode 100644 index 0000000000000..5d7ea053b66da --- /dev/null +++ b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js @@ -0,0 +1,49 @@ +/* + * 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. + */ +/* eslint-disable max-len */ + +import { i18n } from '@kbn/i18n'; + +export const getWebLogsSavedObjects = () => { + return [ + { + 'id': 'de71f4f0-1902-11e9-919b-ffe5949a18d2', + 'type': 'map', + 'updated_at': '2019-01-15T20:30:25.436Z', + 'version': 5, + 'references': [ + { + 'name': 'layer_1_join_0_index_pattern', + 'type': 'index-pattern', + 'id': '90943e30-9a47-11e8-b64d-95841ca0b247' + }, + { + 'name': 'layer_2_source_index_pattern', + 'type': 'index-pattern', + 'id': '90943e30-9a47-11e8-b64d-95841ca0b247' + }, + { + 'name': 'layer_3_source_index_pattern', + 'type': 'index-pattern', + 'id': '90943e30-9a47-11e8-b64d-95841ca0b247' + } + ], + 'migrationVersion': { + 'map': '7.1.0' + }, + 'attributes': { + 'title': i18n.translate('xpack.maps.sampleData.flightaSpec.logsTitle', { + defaultMessage: '[Logs] Total Requests and Bytes' + }), + 'description': '', + 'mapStateJSON': '{"zoom":3.64,"center":{"lon":-88.92107,"lat":42.16337},"timeFilters":{"from":"now-7d","to":"now"},"refreshConfig":{"isPaused":true,"interval":0},"query":{"language":"kuery","query":""}}', + 'layerListJSON': '[{"id":"0hmz5","alpha":1,"sourceDescriptor":{"type":"EMS_TMS","id":"road_map"},"visible":true,"style":{"type":"TILE","properties":{}},"type":"TILE","minZoom":0,"maxZoom":24},{"id":"edh66","label":"Total Requests by Country","minZoom":0,"maxZoom":24,"alpha":0.5,"sourceDescriptor":{"type":"EMS_FILE","id":"world_countries"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"count of kibana_sample_data_logs:geo.src","name":"__kbnjoin__count_groupby_kibana_sample_data_logs.geo.src","origin":"join"},"color":"Greys"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"STATIC","options":{"size":10}}}},"type":"VECTOR","joins":[{"leftField":"iso2","right":{"id":"673ff994-fc75-4c67-909b-69fcb0e1060e","indexPatternTitle":"kibana_sample_data_logs","term":"geo.src","indexPatternRefName":"layer_1_join_0_index_pattern"}}]},{"id":"gaxya","label":"Actual Requests","minZoom":9,"maxZoom":24,"alpha":1,"sourceDescriptor":{"id":"b7486535-171b-4d3b-bb2e-33c1a0a2854c","type":"ES_SEARCH","geoField":"geo.coordinates","limit":2048,"filterByMapBounds":true,"tooltipProperties":["clientip","timestamp","host","request","response","machine.os","agent","bytes"],"indexPatternRefName":"layer_2_source_index_pattern"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"STATIC","options":{"color":"#2200ff"}},"lineColor":{"type":"STATIC","options":{"color":"#FFFFFF"}},"lineWidth":{"type":"STATIC","options":{"size":2}},"iconSize":{"type":"DYNAMIC","options":{"field":{"label":"bytes","name":"bytes","origin":"source"},"minSize":1,"maxSize":23}}}},"type":"VECTOR"},{"id":"tfi3f","label":"Total Requests and Bytes","minZoom":0,"maxZoom":9,"alpha":1,"sourceDescriptor":{"type":"ES_GEO_GRID","resolution":"COARSE","id":"8aaa65b5-a4e9-448b-9560-c98cb1c5ac5b","geoField":"geo.coordinates","requestType":"point","metrics":[{"type":"count"},{"type":"sum","field":"bytes"}],"indexPatternRefName":"layer_3_source_index_pattern"},"visible":true,"style":{"type":"VECTOR","properties":{"fillColor":{"type":"DYNAMIC","options":{"field":{"label":"Count","name":"doc_count","origin":"source"},"color":"Blues"}},"lineColor":{"type":"STATIC","options":{"color":"#cccccc"}},"lineWidth":{"type":"STATIC","options":{"size":1}},"iconSize":{"type":"DYNAMIC","options":{"field":{"label":"sum of bytes","name":"sum_of_bytes","origin":"source"},"minSize":1,"maxSize":25}}}},"type":"VECTOR"}]', + 'uiStateJSON': '{"isDarkMode":false}', + 'bounds': { 'type': 'envelope', 'coordinates': [[-124.45342, 54.91445], [-53.38872, 26.21461]] } + } + } + ]; +}; diff --git a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.json b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.json deleted file mode 100644 index f446931792bb0..0000000000000 --- a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "id":"de71f4f0-1902-11e9-919b-ffe5949a18d2", - "type":"map", - "updated_at":"2019-01-15T20:30:25.436Z", - "version":5, - "references" : [ - { - "name" : "layer_1_join_0_index_pattern", - "type" : "index-pattern", - "id" : "90943e30-9a47-11e8-b64d-95841ca0b247" - }, - { - "name" : "layer_2_source_index_pattern", - "type" : "index-pattern", - "id" : "90943e30-9a47-11e8-b64d-95841ca0b247" - }, - { - "name" : "layer_3_source_index_pattern", - "type" : "index-pattern", - "id" : "90943e30-9a47-11e8-b64d-95841ca0b247" - } - ], - "migrationVersion" : { - "map" : "7.1.0" - }, - "attributes":{ - "title":"[Logs] Total Requests and Bytes", - "description":"", - "mapStateJSON":"{\"zoom\":3.64,\"center\":{\"lon\":-88.92107,\"lat\":42.16337},\"timeFilters\":{\"from\":\"now-7d\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"language\":\"kuery\",\"query\":\"\"}}", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"edh66\",\"label\":\"Total Requests by Country\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.5,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"world_countries\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"count of kibana_sample_data_logs:geo.src\",\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_logs.geo.src\",\"origin\":\"join\"},\"color\":\"Greys\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"iso2\",\"right\":{\"id\":\"673ff994-fc75-4c67-909b-69fcb0e1060e\",\"indexPatternTitle\":\"kibana_sample_data_logs\",\"term\":\"geo.src\",\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]},{\"id\":\"gaxya\",\"label\":\"Actual Requests\",\"minZoom\":9,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"id\":\"b7486535-171b-4d3b-bb2e-33c1a0a2854c\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[\"clientip\",\"timestamp\",\"host\",\"request\",\"response\",\"machine.os\",\"agent\",\"bytes\"],\"indexPatternRefName\":\"layer_2_source_index_pattern\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#2200ff\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":2}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"bytes\",\"name\":\"bytes\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":23}}}},\"type\":\"VECTOR\"},{\"id\":\"tfi3f\",\"label\":\"Total Requests and Bytes\",\"minZoom\":0,\"maxZoom\":9,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"resolution\":\"COARSE\",\"id\":\"8aaa65b5-a4e9-448b-9560-c98cb1c5ac5b\",\"geoField\":\"geo.coordinates\",\"requestType\":\"point\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"sum\",\"field\":\"bytes\"}],\"indexPatternRefName\":\"layer_3_source_index_pattern\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"sum of bytes\",\"name\":\"sum_of_bytes\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":25}}}},\"type\":\"VECTOR\"}]", - "uiStateJSON":"{\"isDarkMode\":false}", - "bounds":{"type":"envelope","coordinates":[[-124.45342,54.91445],[-53.38872,26.21461]]} - } - } -] From c2c715ff4cfd02b8d53599702b6bf70cad93730c Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 6 Mar 2019 16:31:51 -0800 Subject: [PATCH 19/27] download oss version of kibana in packer cache script (#32588) --- .ci/packer_cache.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/packer_cache.sh b/.ci/packer_cache.sh index 72f34c81f98b8..fa0d0d0743707 100755 --- a/.ci/packer_cache.sh +++ b/.ci/packer_cache.sh @@ -7,6 +7,7 @@ source src/dev/ci_setup/setup.sh; # download es snapshots node scripts/es snapshot --download-only; +node scripts/es snapshot --license=oss --download-only; # download reporting browsers cd "x-pack"; From 751d0deb905e429a9daa9649f85446a39974b248 Mon Sep 17 00:00:00 2001 From: Ganesh Patro <30416891+ganeshpatro321@users.noreply.github.com> Date: Thu, 7 Mar 2019 07:27:13 +0530 Subject: [PATCH 20/27] Added dummy fucntion to avoid warning. (#32070) Co-Authored-By: ganeshpatro321 <30416891+ganeshpatro321@users.noreply.github.com> --- .../components/functional/filter_bar_loading.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/uptime/public/components/functional/filter_bar_loading.tsx b/x-pack/plugins/uptime/public/components/functional/filter_bar_loading.tsx index a10e552440a73..4359c923f83d9 100644 --- a/x-pack/plugins/uptime/public/components/functional/filter_bar_loading.tsx +++ b/x-pack/plugins/uptime/public/components/functional/filter_bar_loading.tsx @@ -15,4 +15,15 @@ const searchBox = { }), }; -export const FilterBarLoading = () => ; +/** + * This component provides a visual placeholder while the FilterBar is loading. + * The onChange prop is required, so we provide an empty function to suppress the warning. + */ +export const FilterBarLoading = () => ( + { + /* */ + }} + /> +); From a87061ad0e3444a297e7cc8eb5180156274c9ff4 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Wed, 6 Mar 2019 22:31:12 -0500 Subject: [PATCH 21/27] [Maps] Move tooltips to store (#32333) This is an internal refactor: - move tooltip management out of layers, and to the mapbox-component - use global handler iso of multiple handlers on individual layers (this did remove the cursor-pointer change, since we no longer are explicitly handling on-enter/leave events). - put tooltip state in store Fixes bugs: - when layer is removed, any corresponding tooltip should be removed as well - when layer is made invisible, any corresponding tooltip should be removed as well --- .../maps/public/actions/store_actions.js | 40 +++- .../maps/public/components/map/mb/index.js | 11 +- .../maps/public/components/map/mb/view.js | 181 +++++++++++------- .../maps/public/selectors/map_selectors.js | 4 + .../public/shared/layers/heatmap_layer.js | 11 +- .../maps/public/shared/layers/layer.js | 8 + .../maps/public/shared/layers/tile_layer.js | 10 +- .../maps/public/shared/layers/vector_layer.js | 131 +++---------- x-pack/plugins/maps/public/store/map.js | 9 +- 9 files changed, 218 insertions(+), 187 deletions(-) diff --git a/x-pack/plugins/maps/public/actions/store_actions.js b/x-pack/plugins/maps/public/actions/store_actions.js index a895655d1e2c8..e38c5e25c086a 100644 --- a/x-pack/plugins/maps/public/actions/store_actions.js +++ b/x-pack/plugins/maps/public/actions/store_actions.js @@ -14,6 +14,7 @@ import { getMapReady, getWaitingForMapReadyLayerListRaw, getTransientLayerId, + getTooltipState } from '../selectors/map_selectors'; import { updateFlyout, FLYOUT_STATE } from '../store/ui'; import { SOURCE_DATA_ID_ORIGIN } from '../../common/constants'; @@ -49,6 +50,7 @@ export const CLEAR_GOTO = 'CLEAR_GOTO'; export const TRACK_CURRENT_LAYER_STATE = 'TRACK_CURRENT_LAYER_STATE'; export const ROLLBACK_TO_TRACKED_LAYER_STATE = 'ROLLBACK_TO_TRACKED_LAYER_STATE'; export const REMOVE_TRACKED_LAYER_STATE = 'REMOVE_TRACKED_LAYER_STATE'; +export const SET_TOOLTIP_STATE = 'SET_TOOLTIP_STATE'; function getLayerLoadingCallbacks(dispatch, layerId) { return { @@ -146,6 +148,15 @@ export function setLayerErrorStatus(layerId, errorMessage) { }; } +export function clearTooltipStateForLayer(layerId) { + return (dispatch, getState) => { + const tooltipState = getTooltipState(getState()); + if (tooltipState && tooltipState.layerId === layerId) { + dispatch(setTooltipState(null)); + } + }; +} + export function toggleLayerVisible(layerId) { return async (dispatch, getState) => { //if the current-state is invisible, we also want to sync data @@ -157,6 +168,11 @@ export function toggleLayerVisible(layerId) { return; } const makeVisible = !layer.isVisible(); + + if (!makeVisible) { + dispatch(clearTooltipStateForLayer(layerId)); + } + await dispatch({ type: TOGGLE_LAYER_VISIBLE, layerId @@ -195,7 +211,7 @@ export function removeTransientLayer() { } export function setTransientLayer(layerId) { - return { + return { type: SET_TRANSIENT_LAYER, transientLayerId: layerId, }; @@ -284,11 +300,18 @@ export function mapExtentChanged(newMapConstants) { ...newMapConstants } }); - const newDataFilters = { ...dataFilters, ...newMapConstants }; + const newDataFilters = { ...dataFilters, ...newMapConstants }; await syncDataForAllLayers(getState, dispatch, newDataFilters); }; } +export function setTooltipState(tooltipState) { + return { + type: 'SET_TOOLTIP_STATE', + tooltipState: tooltipState + }; +} + export function setMouseCoordinates({ lat, lon }) { let safeLon = lon; if (lon > 180) { @@ -463,18 +486,19 @@ export function removeSelectedLayer() { }; } -export function removeLayer(id) { +export function removeLayer(layerId) { return (dispatch, getState) => { const layerGettingRemoved = getLayerList(getState()).find(layer => { - return id === layer.getId(); + return layerId === layer.getId(); }); - if (layerGettingRemoved) { - layerGettingRemoved.destroy(); + if (!layerGettingRemoved) { + return; } - + dispatch(clearTooltipStateForLayer(layerId)); + layerGettingRemoved.destroy(); dispatch({ type: REMOVE_LAYER, - id + id: layerId }); }; } diff --git a/x-pack/plugins/maps/public/components/map/mb/index.js b/x-pack/plugins/maps/public/components/map/mb/index.js index 5236f2fc84f07..7e57eaa7ee336 100644 --- a/x-pack/plugins/maps/public/components/map/mb/index.js +++ b/x-pack/plugins/maps/public/components/map/mb/index.js @@ -13,9 +13,9 @@ import { setMouseCoordinates, clearMouseCoordinates, clearGoto, - setLayerErrorStatus, + setTooltipState } from '../../../actions/store_actions'; -import { getLayerList, getMapReady, getGoto } from '../../../selectors/map_selectors'; +import { getTooltipState, getLayerList, getMapReady, getGoto } from '../../../selectors/map_selectors'; import { getInspectorAdapters } from '../../../store/non_serializable_instances'; function mapStateToProps(state = {}) { @@ -24,6 +24,7 @@ function mapStateToProps(state = {}) { layerList: getLayerList(state), goto: getGoto(state), inspectorAdapters: getInspectorAdapters(state), + tooltipState: getTooltipState(state) }; } @@ -49,8 +50,10 @@ function mapDispatchToProps(dispatch) { clearGoto: () => { dispatch(clearGoto()); }, - setLayerErrorStatus: (id, msg) => - dispatch(setLayerErrorStatus(id, msg)) + setTooltipState(tooltipState) { + dispatch(setTooltipState(tooltipState)); + } + }; } diff --git a/x-pack/plugins/maps/public/components/map/mb/view.js b/x-pack/plugins/maps/public/components/map/mb/view.js index 0729557dc86f6..6421ca54ee66c 100644 --- a/x-pack/plugins/maps/public/components/map/mb/view.js +++ b/x-pack/plugins/maps/public/components/map/mb/view.js @@ -6,17 +6,23 @@ import _ from 'lodash'; import React from 'react'; +import ReactDOM from 'react-dom'; import { ResizeChecker } from 'ui/resize_checker'; import { syncLayerOrder, removeOrphanedSourcesAndLayers, createMbMapInstance } from './utils'; import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants'; import mapboxgl from 'mapbox-gl'; +import { FeatureTooltip } from '../feature_tooltip'; export class MBMapContainer extends React.Component { constructor() { super(); this._mbMap = null; - this._listeners = new Map(); // key is mbLayerId, value eventHandlers map + this._tooltipContainer = document.createElement('div'); + this._mbPopup = new mapboxgl.Popup({ + closeButton: false, + closeOnClick: false, + }); } _debouncedSync = _.debounce(() => { @@ -26,6 +32,61 @@ export class MBMapContainer extends React.Component { } }, 256); + _updateTooltipState = _.debounce(async (e) => { + + const mbLayerIds = this._getMbLayerIdsForTooltips(); + const features = this._mbMap.queryRenderedFeatures(e.point, { layers: mbLayerIds }); + + if (!features.length) { + this.props.setTooltipState(null); + return; + } + + const targetFeature = features[0]; + if (this.props.tooltipState) { + const propertiesUnchanged = _.isEqual(this.props.tooltipState.activeFeature.properties, targetFeature.properties); + const geometryUnchanged = _.isEqual(this.props.tooltipState.activeFeature.geometry, targetFeature.geometry); + if(propertiesUnchanged && geometryUnchanged) { + return; + } + } + + const layer = this._getLayer(targetFeature.layer.id); + const formattedProperties = await layer.getPropertiesForTooltip(targetFeature.properties); + + let popupAnchorLocation = [e.lngLat.lng, e.lngLat.lat]; // default popup location to mouse location + if (targetFeature.geometry.type === 'Point') { + const coordinates = targetFeature.geometry.coordinates.slice(); + + // Ensure that if the map is zoomed out such that multiple + // copies of the feature are visible, the popup appears + // over the copy being pointed to. + while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) { + coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360; + } + + popupAnchorLocation = coordinates; + } + + this.props.setTooltipState({ + activeFeature: { + properties: targetFeature.properties, + geometry: targetFeature.geometry + }, + formattedProperties: formattedProperties, + layerId: layer.getId(), + location: popupAnchorLocation + }); + + }, 100); + + + _getMbLayerIdsForTooltips() { + return this.props.layerList.reduce((mbLayerIds, layer) => { + return layer.canShowTooltip() ? mbLayerIds.concat(layer.getMbLayerIds()) : mbLayerIds; + }, []); + } + _getMapState() { const zoom = this._mbMap.getZoom(); const mbCenter = this._mbMap.getCenter(); @@ -45,6 +106,13 @@ export class MBMapContainer extends React.Component { }; } + componentDidUpdate() { + // do not debounce syncing of map-state and tooltip + this._syncMbMapWithMapState(); + this._syncTooltipState(); + this._debouncedSync(); + } + componentDidMount() { this._initializeMap(); this._isMounted = true; @@ -58,6 +126,7 @@ export class MBMapContainer extends React.Component { if (this._mbMap) { this._mbMap.remove(); this._mbMap = null; + this._tooltipContainer = null; } this.props.onMapDestroyed(); } @@ -70,29 +139,6 @@ export class MBMapContainer extends React.Component { return; } - // Override mapboxgl.Map "on" and "removeLayer" methods so we can track layer listeners - // Tracked layer listerners are used to clean up event handlers - const originalMbBoxOnFunc = this._mbMap.on; - const originalMbBoxRemoveLayerFunc = this._mbMap.removeLayer; - this._mbMap.on = (...args) => { - // args do not identify layer so there is nothing to track - if (args.length <= 2) { - originalMbBoxOnFunc.apply(this._mbMap, args); - return; - } - - const eventType = args[0]; - const mbLayerId = args[1]; - const handler = args[2]; - this._addListener(eventType, mbLayerId, handler); - - originalMbBoxOnFunc.apply(this._mbMap, args); - }; - this._mbMap.removeLayer = (id) => { - this._removeListeners(id); - originalMbBoxRemoveLayerFunc.apply(this._mbMap, [id]); - }; - this._initResizerChecker(); // moveend callback is debounced to avoid updating map extent state while map extent is still changing @@ -115,44 +161,48 @@ export class MBMapContainer extends React.Component { this.props.clearMouseCoordinates(); }); + + this._mbMap.on('mousemove', this._updateTooltipState); + this.props.onMapReady(this._getMapState()); } - _addListener(eventType, mbLayerId, handler) { - this._removeListener(eventType, mbLayerId); - - const eventHandlers = !this._listeners.has(mbLayerId) - ? new Map() - : this._listeners.get(mbLayerId); - eventHandlers.set(eventType, handler); - this._listeners.set(mbLayerId, eventHandlers); + _initResizerChecker() { + this._checker = new ResizeChecker(this.refs.mapContainer); + this._checker.on('resize', () => { + this._mbMap.resize(); + }); } - _removeListeners(mbLayerId) { - if (this._listeners.has(mbLayerId)) { - const eventHandlers = this._listeners.get(mbLayerId); - eventHandlers.forEach((value, eventType) => { - this._removeListener(eventType, mbLayerId); - }); - this._listeners.delete(mbLayerId); + _hideTooltip() { + if (this._mbPopup.isOpen()) { + this._mbPopup.remove(); + ReactDOM.unmountComponentAtNode(this._tooltipContainer); } } - _removeListener(eventType, mbLayerId) { - if (this._listeners.has(mbLayerId)) { - const eventHandlers = this._listeners.get(mbLayerId); - if (eventHandlers.has(eventType)) { - this._mbMap.off(eventType, mbLayerId, eventHandlers.get(eventType)); - eventHandlers.delete(eventType); - } - } + _showTooltip() { + //todo: can still be optimized. No need to rerender if content remains identical + ReactDOM.render( + React.createElement( + FeatureTooltip, { + properties: this.props.tooltipState.formattedProperties, + } + ), + this._tooltipContainer + ); + + this._mbPopup.setLngLat(this.props.tooltipState.location) + .setDOMContent(this._tooltipContainer) + .addTo(this._mbMap); } - _initResizerChecker() { - this._checker = new ResizeChecker(this.refs.mapContainer); - this._checker.on('resize', () => { - this._mbMap.resize(); - }); + _syncTooltipState() { + if (this.props.tooltipState) { + this._showTooltip(); + } else { + this._hideTooltip(); + } } _syncMbMapWithMapState = () => { @@ -188,21 +238,25 @@ export class MBMapContainer extends React.Component { }; + _getLayer(mbLayerId) { + return this.props.layerList.find((layer) => { + const mbLayerIds = layer.getMbLayerIds(); + return mbLayerIds.indexOf(mbLayerId) > -1; + }); + } + _syncMbMapWithLayerList = () => { - const { - isMapReady, - layerList, - } = this.props; - if (!isMapReady) { + if (!this.props.isMapReady) { return; } - removeOrphanedSourcesAndLayers(this._mbMap, layerList); - layerList.forEach(layer => { + removeOrphanedSourcesAndLayers(this._mbMap, this.props.layerList); + this.props.layerList.forEach(layer => { layer.syncLayerWithMB(this._mbMap); }); - syncLayerOrder(this._mbMap, layerList); + + syncLayerOrder(this._mbMap, this.props.layerList); }; _syncMbMapWithInspector = () => { @@ -222,12 +276,7 @@ export class MBMapContainer extends React.Component { }; render() { - // do not debounce syncing zoom and center - this._syncMbMapWithMapState(); - this._debouncedSync(); - return ( -
- ); + return (
); } } diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.js b/x-pack/plugins/maps/public/selectors/map_selectors.js index 55c6f7c4d649c..e8f7af3c7dc4d 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/plugins/maps/public/selectors/map_selectors.js @@ -61,6 +61,10 @@ function createStyleInstance(styleDescriptor) { } } +export const getTooltipState = ({ map }) => { + return map.tooltipState; +}; + export const getMapReady = ({ map }) => map && map.ready; export const getGoto = ({ map }) => map && map.goto; diff --git a/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js b/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js index 639df80b00b28..5bcfafcb2650e 100644 --- a/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js +++ b/x-pack/plugins/maps/public/shared/layers/heatmap_layer.js @@ -45,10 +45,19 @@ export class HeatmapLayer extends AbstractLayer { return metricfields[0].propertyKey; } + + _getMbLayerId() { + return this.getId() + '_heatmap'; + } + + getMbLayerIds() { + return [this._getMbLayerId()]; + } + syncLayerWithMB(mbMap) { const mbSource = mbMap.getSource(this.getId()); - const mbLayerId = this.getId() + '_heatmap'; + const mbLayerId = this._getMbLayerId(); if (!mbSource) { mbMap.addSource(this.getId(), { diff --git a/x-pack/plugins/maps/public/shared/layers/layer.js b/x-pack/plugins/maps/public/shared/layers/layer.js index db3dd4b05625e..899177d1008dc 100644 --- a/x-pack/plugins/maps/public/shared/layers/layer.js +++ b/x-pack/plugins/maps/public/shared/layers/layer.js @@ -165,6 +165,14 @@ export class AbstractLayer { //no-op by default } + getMbLayerIds() { + throw new Error('Should implement AbstractLayer#getMbLayerIds'); + } + + canShowTooltip() { + return false; + } + syncLayerWithMb() { //no-op by default } diff --git a/x-pack/plugins/maps/public/shared/layers/tile_layer.js b/x-pack/plugins/maps/public/shared/layers/tile_layer.js index 296d7b64f0d4a..2f58252c7dd44 100644 --- a/x-pack/plugins/maps/public/shared/layers/tile_layer.js +++ b/x-pack/plugins/maps/public/shared/layers/tile_layer.js @@ -49,10 +49,18 @@ export class TileLayer extends AbstractLayer { } } + _getMbLayerId() { + return this.getId() + '_raster'; + } + + getMbLayerIds() { + return [this._getMbLayerId()]; + } + syncLayerWithMB(mbMap) { const source = mbMap.getSource(this.getId()); - const mbLayerId = this.getId() + '_raster'; + const mbLayerId = this._getMbLayerId(); if (!source) { const sourceDataRequest = this.getSourceDataRequest(); diff --git a/x-pack/plugins/maps/public/shared/layers/vector_layer.js b/x-pack/plugins/maps/public/shared/layers/vector_layer.js index bdd4368214206..d3076de4852f3 100644 --- a/x-pack/plugins/maps/public/shared/layers/vector_layer.js +++ b/x-pack/plugins/maps/public/shared/layers/vector_layer.js @@ -4,16 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import mapboxgl from 'mapbox-gl'; import turf from 'turf'; -import React from 'react'; -import ReactDOM from 'react-dom'; - import { AbstractLayer } from './layer'; import { VectorStyle } from './styles/vector_style'; import { LeftInnerJoin } from './joins/left_inner_join'; import { SOURCE_DATA_ID_ORIGIN } from '../../../common/constants'; -import { FeatureTooltip } from '../../components/map/feature_tooltip'; import _ from 'lodash'; const EMPTY_FEATURE_COLLECTION = { @@ -25,13 +20,6 @@ export class VectorLayer extends AbstractLayer { static type = 'VECTOR'; - static popup = new mapboxgl.Popup({ - closeButton: false, - closeOnClick: false, - }); - - static tooltipContainer = document.createElement('div'); - static createDescriptor(options, mapColors) { const layerDescriptor = super.createDescriptor(options); layerDescriptor.type = VectorLayer.type; @@ -171,10 +159,7 @@ export class VectorLayer extends AbstractLayer { !isGeoGridPrecisionAware ) { const sourceDataRequest = this._findDataRequestForSource(sourceDataId); - if (sourceDataRequest && sourceDataRequest.hasDataOrRequestInProgress()) { - return true; - } - return false; + return (sourceDataRequest && sourceDataRequest.hasDataOrRequestInProgress()); } const sourceDataRequest = this._findDataRequestForSource(sourceDataId); @@ -397,7 +382,7 @@ export class VectorLayer extends AbstractLayer { _setMbPointsProperties(mbMap) { const sourceId = this.getId(); - const pointLayerId = this.getId() + '_circle'; + const pointLayerId = this._getMbPointLayerId(); const pointLayer = mbMap.getLayer(pointLayerId); if (!pointLayer) { mbMap.addLayer({ @@ -415,13 +400,12 @@ export class VectorLayer extends AbstractLayer { }); mbMap.setLayoutProperty(pointLayerId, 'visibility', this.isVisible() ? 'visible' : 'none'); mbMap.setLayerZoomRange(pointLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); - this._addTooltipListeners(mbMap, pointLayerId); } _setMbLinePolygonProperties(mbMap) { const sourceId = this.getId(); - const fillLayerId = this.getId() + '_fill'; - const lineLayerId = this.getId() + '_line'; + const fillLayerId = this._getMbPolygonLayerId(); + const lineLayerId = this._getMbLineLayerId(); if (!mbMap.getLayer(fillLayerId)) { mbMap.addLayer({ id: fillLayerId, @@ -462,7 +446,6 @@ export class VectorLayer extends AbstractLayer { mbMap.setLayoutProperty(lineLayerId, 'visibility', this.isVisible() ? 'visible' : 'none'); mbMap.setLayerZoomRange(lineLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); mbMap.setLayerZoomRange(fillLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); - this._addTooltipListeners(mbMap, fillLayerId); } _syncStylePropertiesWithMb(mbMap) { @@ -493,101 +476,37 @@ export class VectorLayer extends AbstractLayer { }); } - _canShowTooltips() { - return this._source.canFormatFeatureProperties(); + _getMbPointLayerId() { + return this.getId() + '_circle'; + } + + _getMbLineLayerId() { + return this.getId() + '_line'; + } + + _getMbPolygonLayerId() { + return this.getId() + '_fill'; } - async _getPropertiesForTooltip(feature) { - const tooltipsFromSource = await this._source.filterAndFormatProperties(feature.properties); + getMbLayerIds() { + return [this._getMbPointLayerId(), this._getMbLineLayerId(), this._getMbPolygonLayerId()]; + } + + async getPropertiesForTooltip(properties) { + const tooltipsFromSource = await this._source.filterAndFormatProperties(properties); //add tooltips from joins - const allProps = this._joins.reduce((acc, join) => { - const propsFromJoin = join.filterAndFormatPropertiesForTooltip(feature.properties); + return this._joins.reduce((acc, join) => { + const propsFromJoin = join.filterAndFormatPropertiesForTooltip(properties); return { ...propsFromJoin, ...acc, }; }, { ...tooltipsFromSource }); - - return allProps; } - _addTooltipListeners(mbMap, mbLayerId) { - - if (!this._canShowTooltips()) { - return; - } - - const showTooltip = async (feature, eventLngLat) => { - let popupAnchorLocation = eventLngLat; // default popup location to mouse location - if (feature.geometry.type === 'Point') { - const coordinates = feature.geometry.coordinates.slice(); - - // Ensure that if the map is zoomed out such that multiple - // copies of the feature are visible, the popup appears - // over the copy being pointed to. - while (Math.abs(eventLngLat.lng - coordinates[0]) > 180) { - coordinates[0] += eventLngLat.lng > coordinates[0] ? 360 : -360; - } - - popupAnchorLocation = coordinates; - } - - const properties = await this._getPropertiesForTooltip(feature); - - ReactDOM.render( - React.createElement( - FeatureTooltip, { - properties: properties, - } - ), - VectorLayer.tooltipContainer - ); - - VectorLayer.popup.setLngLat(popupAnchorLocation) - .setDOMContent(VectorLayer.tooltipContainer) - .addTo(mbMap); - }; - - let activeFeature; - let isTooltipOpen = false; - mbMap.on('mousemove', mbLayerId, _.debounce((e) => { - if (!isTooltipOpen) { - return; - } - - const features = mbMap.queryRenderedFeatures(e.point) - .filter(feature => { - return feature.layer.source === this.getId(); - }); - if (features.length === 0) { - return; - } - - const propertiesUnchanged = _.isEqual(activeFeature.properties, features[0].properties); - const geometryUnchanged = _.isEqual(activeFeature.geometry, features[0].geometry); - if(propertiesUnchanged && geometryUnchanged) { - // mouse over same feature, no need to update tooltip - return; - } - - activeFeature = features[0]; - showTooltip(activeFeature, e.lngLat); - }, 100)); - - mbMap.on('mouseenter', mbLayerId, (e) => { - isTooltipOpen = true; - mbMap.getCanvas().style.cursor = 'pointer'; - - activeFeature = e.features[0]; - showTooltip(activeFeature, e.lngLat); - }); - - mbMap.on('mouseleave', mbLayerId, () => { - isTooltipOpen = false; - mbMap.getCanvas().style.cursor = ''; - VectorLayer.popup.remove(); - ReactDOM.unmountComponentAtNode(VectorLayer.tooltipContainer); - }); + canShowTooltip() { + return this._source.canFormatFeatureProperties(); } + } diff --git a/x-pack/plugins/maps/public/store/map.js b/x-pack/plugins/maps/public/store/map.js index 0601d5efaaea2..5b5f564b333f4 100644 --- a/x-pack/plugins/maps/public/store/map.js +++ b/x-pack/plugins/maps/public/store/map.js @@ -35,7 +35,8 @@ import { TRACK_CURRENT_LAYER_STATE, ROLLBACK_TO_TRACKED_LAYER_STATE, REMOVE_TRACKED_LAYER_STATE, - UPDATE_SOURCE_DATA_REQUEST + UPDATE_SOURCE_DATA_REQUEST, + SET_TOOLTIP_STATE } from '../actions/store_actions'; import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from './util'; @@ -84,6 +85,7 @@ const updateLayerSourceDescriptorProp = (state, layerId, propName, value) => { const INITIAL_STATE = { ready: false, goto: null, + tooltipState: null, mapState: { zoom: 4, center: { @@ -114,6 +116,11 @@ export function map(state = INITIAL_STATE, action) { return trackCurrentLayerState(state, action.layerId); case ROLLBACK_TO_TRACKED_LAYER_STATE: return rollbackTrackedLayerState(state, action.layerId); + case SET_TOOLTIP_STATE: + return { + ...state, + tooltipState: action.tooltipState + }; case SET_MOUSE_COORDINATES: return { ...state, From 41adcfdb5023e94a69d678a26b2f5332525e688a Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Wed, 6 Mar 2019 23:36:12 -0700 Subject: [PATCH 22/27] Initial unit tests for embedded_visualize_handler (#31324) --- .../embedded_visualize_handler.test.ts.snap | 28 ++ .../loader/embedded_visualize_handler.test.ts | 290 ++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap create mode 100644 src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts diff --git a/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap b/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap new file mode 100644 index 0000000000000..6e9ab0a1aa223 --- /dev/null +++ b/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EmbeddedVisualizeHandler data$ observable can be used to get response data in the correct format 1`] = ` +Object { + "params": Object {}, + "visConfig": Object {}, + "visData": Object {}, + "visType": "histogram", +} +`; + +exports[`EmbeddedVisualizeHandler update should add provided data- attributes to the html element 1`] = ` +
+`; + +exports[`EmbeddedVisualizeHandler update should remove null data- attributes from the html element 1`] = ` +
+`; diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts new file mode 100644 index 0000000000000..02a00687c1dad --- /dev/null +++ b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts @@ -0,0 +1,290 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +jest.useFakeTimers(); + +import { EventEmitter } from 'events'; + +// @ts-ignore +import MockState from '../../../../../fixtures/mock_state'; +import { Adapters } from '../../inspector/types'; +import { RequestHandlerParams, Vis } from '../../vis'; +import { VisResponseData } from './types'; + +jest.mock('./utils', () => ({ + queryGeohashBounds: jest.fn(), +})); + +jest.mock('./pipeline_helpers/utilities', () => ({ + getFormat: jest.fn(), + getTableAggs: jest.fn(), +})); + +const timefilter = new EventEmitter(); +jest.mock('../../timefilter', () => ({ timefilter })); + +const mockInspectorIsAvailable = jest.fn(); +const mockInspectorOpen = jest.fn(); +jest.mock('../../inspector', () => ({ + Inspector: { + open: (adapters: Adapters, opts: any) => mockInspectorOpen(adapters, opts), + isAvailable: (adapters: Adapters) => mockInspectorIsAvailable(adapters), + }, +})); + +const mockDataLoaderFetch = jest.fn().mockReturnValue({ + as: 'visualization', + value: { + visType: 'histogram', + visData: {}, + visConfig: {}, + params: {}, + }, +}); +const MockDataLoader = class { + public async fetch(data: any) { + return await mockDataLoaderFetch(data); + } +}; + +jest.mock('./pipeline_data_loader', () => ({ + PipelineDataLoader: MockDataLoader, +})); +jest.mock('./visualize_data_loader', () => ({ + VisualizeDataLoader: MockDataLoader, +})); + +import { EmbeddedVisualizeHandler } from './embedded_visualize_handler'; + +describe('EmbeddedVisualizeHandler', () => { + let handler: any; + let div: HTMLElement; + let dataLoaderParams: RequestHandlerParams; + const mockVis: Vis = { + title: 'My Vis', + // @ts-ignore + type: 'foo', + getAggConfig: () => [], + _setUiState: () => ({}), + getUiState: () => new MockState(), + on: () => ({}), + off: () => ({}), + removeListener: jest.fn(), + API: {}, + }; + + beforeEach(() => { + mockDataLoaderFetch.mockClear(); + mockInspectorOpen.mockClear(); + mockInspectorIsAvailable.mockClear(); + + dataLoaderParams = { + aggs: [], + filters: undefined, + forceFetch: false, + inspectorAdapters: {}, + query: undefined, + queryFilter: null, + searchSource: undefined, + timeRange: undefined, + uiState: undefined, + }; + + div = document.createElement('div'); + handler = new EmbeddedVisualizeHandler( + div, + { + vis: mockVis, + title: 'My Vis', + searchSource: undefined, + destroy: () => ({}), + }, + { + autoFetch: true, + Private: (provider: () => T) => provider(), + queryFilter: null, + } + ); + }); + + describe('autoFetch', () => { + it('should trigger a reload when autoFetch=true and auto refresh happens', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + timefilter.emit('autoRefreshFetch'); + jest.runAllTimers(); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(true); + }); + + it('should not trigger a reload when autoFetch=false and auto refresh happens', () => { + handler = new EmbeddedVisualizeHandler( + div, + { + vis: mockVis, + title: 'My Vis', + searchSource: undefined, + destroy: () => ({}), + }, + { + autoFetch: false, + Private: (provider: () => T) => provider(), + queryFilter: null, + } + ); + const spy = jest.spyOn(handler, 'fetchAndRender'); + timefilter.emit('autoRefreshFetch'); + jest.runAllTimers(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('getElement', () => { + it('should return the provided html element', () => { + expect(handler.getElement()).toBe(div); + }); + }); + + describe('update', () => { + it('should add provided data- attributes to the html element', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + const params = { + dataAttrs: { foo: 'bar' }, + }; + handler.update(params); + expect(spy).not.toHaveBeenCalled(); + expect(handler.getElement()).toMatchSnapshot(); + }); + + it('should remove null data- attributes from the html element', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + handler.update({ + dataAttrs: { foo: 'bar' }, + }); + const params = { + dataAttrs: { + foo: null, + baz: 'qux', + }, + }; + handler.update(params); + expect(spy).not.toHaveBeenCalled(); + expect(handler.getElement()).toMatchSnapshot(); + }); + + it('should call dataLoader.render with updated timeRange', () => { + const params = { timeRange: { foo: 'bar' } }; + handler.update(params); + jest.runAllTimers(); + expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); + }); + + it('should call dataLoader.render with updated filters', () => { + const params = { filters: [{ foo: 'bar' }] }; + handler.update(params); + jest.runAllTimers(); + expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); + }); + + it('should call dataLoader.render with updated query', () => { + const params = { query: { foo: 'bar' } }; + handler.update(params); + jest.runAllTimers(); + expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); + }); + }); + + describe('destroy', () => { + it('should remove vis event listeners', () => { + const spy = jest.spyOn(mockVis, 'removeListener'); + handler.destroy(); + expect(spy).toHaveBeenCalledTimes(2); + expect(spy.mock.calls[0][0]).toBe('reload'); + expect(spy.mock.calls[1][0]).toBe('update'); + }); + + it('should remove element event listeners', () => { + const spy = jest.spyOn(handler.getElement(), 'removeEventListener'); + handler.destroy(); + expect(spy).toHaveBeenCalled(); + }); + + it('should prevent subsequent renders', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + handler.destroy(); + expect(spy).not.toHaveBeenCalled(); + }); + + it('should cancel debounced fetchAndRender', () => { + const spy = jest.spyOn(handler.debouncedFetchAndRender, 'cancel'); + handler.destroy(); + expect(spy).toHaveBeenCalledTimes(1); + }); + }); + + describe('openInspector', () => { + it('calls Inspector.open()', () => { + handler.openInspector(); + expect(mockInspectorOpen).toHaveBeenCalledTimes(1); + expect(mockInspectorOpen).toHaveBeenCalledWith({}, { title: 'My Vis' }); + }); + }); + + describe('hasInspector', () => { + it('calls Inspector.isAvailable()', () => { + handler.hasInspector(); + expect(mockInspectorIsAvailable).toHaveBeenCalledTimes(1); + expect(mockInspectorIsAvailable).toHaveBeenCalledWith({}); + }); + }); + + describe('reload', () => { + it('should force fetch and render', () => { + const spy = jest.spyOn(handler, 'fetchAndRender'); + handler.reload(); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(true); + }); + }); + + describe('data$', () => { + it('observable can be used to get response data in the correct format', async () => { + let response; + handler.data$.subscribe((data: VisResponseData) => (response = data)); + await handler.fetch(true); + jest.runAllTimers(); + expect(response).toMatchSnapshot(); + }); + }); + + describe('render', () => { + // TODO + }); + + describe('whenFirstRenderComplete', () => { + // TODO + }); + + describe('addRenderCompleteListener', () => { + // TODO + }); + + describe('removeRenderCompleteListener', () => { + // TODO + }); +}); From 7298fc1ddc21035f28af0d84ab530138209b9b43 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 6 Mar 2019 23:47:55 -0800 Subject: [PATCH 23/27] Revert "Initial unit tests for embedded_visualize_handler" (#32615) Reverts elastic/kibana#31324, failing on master --- .../embedded_visualize_handler.test.ts.snap | 28 -- .../loader/embedded_visualize_handler.test.ts | 290 ------------------ 2 files changed, 318 deletions(-) delete mode 100644 src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap delete mode 100644 src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts diff --git a/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap b/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap deleted file mode 100644 index 6e9ab0a1aa223..0000000000000 --- a/src/legacy/ui/public/visualize/loader/__snapshots__/embedded_visualize_handler.test.ts.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EmbeddedVisualizeHandler data$ observable can be used to get response data in the correct format 1`] = ` -Object { - "params": Object {}, - "visConfig": Object {}, - "visData": Object {}, - "visType": "histogram", -} -`; - -exports[`EmbeddedVisualizeHandler update should add provided data- attributes to the html element 1`] = ` -
-`; - -exports[`EmbeddedVisualizeHandler update should remove null data- attributes from the html element 1`] = ` -
-`; diff --git a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts b/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts deleted file mode 100644 index 02a00687c1dad..0000000000000 --- a/src/legacy/ui/public/visualize/loader/embedded_visualize_handler.test.ts +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.useFakeTimers(); - -import { EventEmitter } from 'events'; - -// @ts-ignore -import MockState from '../../../../../fixtures/mock_state'; -import { Adapters } from '../../inspector/types'; -import { RequestHandlerParams, Vis } from '../../vis'; -import { VisResponseData } from './types'; - -jest.mock('./utils', () => ({ - queryGeohashBounds: jest.fn(), -})); - -jest.mock('./pipeline_helpers/utilities', () => ({ - getFormat: jest.fn(), - getTableAggs: jest.fn(), -})); - -const timefilter = new EventEmitter(); -jest.mock('../../timefilter', () => ({ timefilter })); - -const mockInspectorIsAvailable = jest.fn(); -const mockInspectorOpen = jest.fn(); -jest.mock('../../inspector', () => ({ - Inspector: { - open: (adapters: Adapters, opts: any) => mockInspectorOpen(adapters, opts), - isAvailable: (adapters: Adapters) => mockInspectorIsAvailable(adapters), - }, -})); - -const mockDataLoaderFetch = jest.fn().mockReturnValue({ - as: 'visualization', - value: { - visType: 'histogram', - visData: {}, - visConfig: {}, - params: {}, - }, -}); -const MockDataLoader = class { - public async fetch(data: any) { - return await mockDataLoaderFetch(data); - } -}; - -jest.mock('./pipeline_data_loader', () => ({ - PipelineDataLoader: MockDataLoader, -})); -jest.mock('./visualize_data_loader', () => ({ - VisualizeDataLoader: MockDataLoader, -})); - -import { EmbeddedVisualizeHandler } from './embedded_visualize_handler'; - -describe('EmbeddedVisualizeHandler', () => { - let handler: any; - let div: HTMLElement; - let dataLoaderParams: RequestHandlerParams; - const mockVis: Vis = { - title: 'My Vis', - // @ts-ignore - type: 'foo', - getAggConfig: () => [], - _setUiState: () => ({}), - getUiState: () => new MockState(), - on: () => ({}), - off: () => ({}), - removeListener: jest.fn(), - API: {}, - }; - - beforeEach(() => { - mockDataLoaderFetch.mockClear(); - mockInspectorOpen.mockClear(); - mockInspectorIsAvailable.mockClear(); - - dataLoaderParams = { - aggs: [], - filters: undefined, - forceFetch: false, - inspectorAdapters: {}, - query: undefined, - queryFilter: null, - searchSource: undefined, - timeRange: undefined, - uiState: undefined, - }; - - div = document.createElement('div'); - handler = new EmbeddedVisualizeHandler( - div, - { - vis: mockVis, - title: 'My Vis', - searchSource: undefined, - destroy: () => ({}), - }, - { - autoFetch: true, - Private: (provider: () => T) => provider(), - queryFilter: null, - } - ); - }); - - describe('autoFetch', () => { - it('should trigger a reload when autoFetch=true and auto refresh happens', () => { - const spy = jest.spyOn(handler, 'fetchAndRender'); - timefilter.emit('autoRefreshFetch'); - jest.runAllTimers(); - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith(true); - }); - - it('should not trigger a reload when autoFetch=false and auto refresh happens', () => { - handler = new EmbeddedVisualizeHandler( - div, - { - vis: mockVis, - title: 'My Vis', - searchSource: undefined, - destroy: () => ({}), - }, - { - autoFetch: false, - Private: (provider: () => T) => provider(), - queryFilter: null, - } - ); - const spy = jest.spyOn(handler, 'fetchAndRender'); - timefilter.emit('autoRefreshFetch'); - jest.runAllTimers(); - expect(spy).not.toHaveBeenCalled(); - }); - }); - - describe('getElement', () => { - it('should return the provided html element', () => { - expect(handler.getElement()).toBe(div); - }); - }); - - describe('update', () => { - it('should add provided data- attributes to the html element', () => { - const spy = jest.spyOn(handler, 'fetchAndRender'); - const params = { - dataAttrs: { foo: 'bar' }, - }; - handler.update(params); - expect(spy).not.toHaveBeenCalled(); - expect(handler.getElement()).toMatchSnapshot(); - }); - - it('should remove null data- attributes from the html element', () => { - const spy = jest.spyOn(handler, 'fetchAndRender'); - handler.update({ - dataAttrs: { foo: 'bar' }, - }); - const params = { - dataAttrs: { - foo: null, - baz: 'qux', - }, - }; - handler.update(params); - expect(spy).not.toHaveBeenCalled(); - expect(handler.getElement()).toMatchSnapshot(); - }); - - it('should call dataLoader.render with updated timeRange', () => { - const params = { timeRange: { foo: 'bar' } }; - handler.update(params); - jest.runAllTimers(); - expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); - }); - - it('should call dataLoader.render with updated filters', () => { - const params = { filters: [{ foo: 'bar' }] }; - handler.update(params); - jest.runAllTimers(); - expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); - }); - - it('should call dataLoader.render with updated query', () => { - const params = { query: { foo: 'bar' } }; - handler.update(params); - jest.runAllTimers(); - expect(mockDataLoaderFetch).toHaveBeenCalledWith({ ...dataLoaderParams, ...params }); - }); - }); - - describe('destroy', () => { - it('should remove vis event listeners', () => { - const spy = jest.spyOn(mockVis, 'removeListener'); - handler.destroy(); - expect(spy).toHaveBeenCalledTimes(2); - expect(spy.mock.calls[0][0]).toBe('reload'); - expect(spy.mock.calls[1][0]).toBe('update'); - }); - - it('should remove element event listeners', () => { - const spy = jest.spyOn(handler.getElement(), 'removeEventListener'); - handler.destroy(); - expect(spy).toHaveBeenCalled(); - }); - - it('should prevent subsequent renders', () => { - const spy = jest.spyOn(handler, 'fetchAndRender'); - handler.destroy(); - expect(spy).not.toHaveBeenCalled(); - }); - - it('should cancel debounced fetchAndRender', () => { - const spy = jest.spyOn(handler.debouncedFetchAndRender, 'cancel'); - handler.destroy(); - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('openInspector', () => { - it('calls Inspector.open()', () => { - handler.openInspector(); - expect(mockInspectorOpen).toHaveBeenCalledTimes(1); - expect(mockInspectorOpen).toHaveBeenCalledWith({}, { title: 'My Vis' }); - }); - }); - - describe('hasInspector', () => { - it('calls Inspector.isAvailable()', () => { - handler.hasInspector(); - expect(mockInspectorIsAvailable).toHaveBeenCalledTimes(1); - expect(mockInspectorIsAvailable).toHaveBeenCalledWith({}); - }); - }); - - describe('reload', () => { - it('should force fetch and render', () => { - const spy = jest.spyOn(handler, 'fetchAndRender'); - handler.reload(); - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith(true); - }); - }); - - describe('data$', () => { - it('observable can be used to get response data in the correct format', async () => { - let response; - handler.data$.subscribe((data: VisResponseData) => (response = data)); - await handler.fetch(true); - jest.runAllTimers(); - expect(response).toMatchSnapshot(); - }); - }); - - describe('render', () => { - // TODO - }); - - describe('whenFirstRenderComplete', () => { - // TODO - }); - - describe('addRenderCompleteListener', () => { - // TODO - }); - - describe('removeRenderCompleteListener', () => { - // TODO - }); -}); From 9d7cfa55e8ca4b153a588699812694ec6dfd9f00 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 7 Mar 2019 10:48:28 +0300 Subject: [PATCH 24/27] [deprecation warnings] Use of the joda time method [getHourOfDay()] is deprecated. Use [getHour()] instead. (#32551) --- src/legacy/server/sample_data/data_sets/logs/saved_objects.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/server/sample_data/data_sets/logs/saved_objects.js b/src/legacy/server/sample_data/data_sets/logs/saved_objects.js index c1434c4c054ff..f3c9e5f3a581d 100644 --- a/src/legacy/server/sample_data/data_sets/logs/saved_objects.js +++ b/src/legacy/server/sample_data/data_sets/logs/saved_objects.js @@ -241,7 +241,7 @@ export const getSavedObjects = () => [ "attributes": { "title": "kibana_sample_data_logs", "timeFieldName": "timestamp", - "fields": "[{\"name\":\"message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hour_of_day\",\"type\":\"number\",\"count\":0,\"scripted\":true,\"script\":\"doc['timestamp'].value.getHourOfDay()\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", + "fields": "[{\"name\":\"message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hour_of_day\",\"type\":\"number\",\"count\":0,\"scripted\":true,\"script\":\"doc['timestamp'].value.getHour()\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", "fieldFormatMap": "{\"hour_of_day\":{}}" } }, From 3ad1c09670d9358f67341772a25c150b61395831 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 7 Mar 2019 11:06:49 +0300 Subject: [PATCH 25/27] [TSVB] Incorrect behavior when filling the 'interval' field with values: s,m,d,M,y (#32542) --- .../metrics/common/interval_regexp.js | 4 +- .../metrics/common/interval_regexp.test.js | 94 +++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/legacy/core_plugins/metrics/common/interval_regexp.test.js diff --git a/src/legacy/core_plugins/metrics/common/interval_regexp.js b/src/legacy/core_plugins/metrics/common/interval_regexp.js index a7c8b05efa6e4..392d2efd4aa94 100644 --- a/src/legacy/core_plugins/metrics/common/interval_regexp.js +++ b/src/legacy/core_plugins/metrics/common/interval_regexp.js @@ -18,6 +18,6 @@ */ import dateMath from '@elastic/datemath'; -export const GTE_INTERVAL_RE = new RegExp(`^>=([\\d\\.]*\\s*(${dateMath.units.join('|')}))$`); -export const INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + dateMath.units.join('|') + ')$'); +export const GTE_INTERVAL_RE = new RegExp(`^>=([\\d\\.]+\\s*(${dateMath.units.join('|')}))$`); +export const INTERVAL_STRING_RE = new RegExp(`^([\\d\\.]+)\\s*(${dateMath.units.join('|')})$`); diff --git a/src/legacy/core_plugins/metrics/common/interval_regexp.test.js b/src/legacy/core_plugins/metrics/common/interval_regexp.test.js new file mode 100644 index 0000000000000..7872d6431c907 --- /dev/null +++ b/src/legacy/core_plugins/metrics/common/interval_regexp.test.js @@ -0,0 +1,94 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { GTE_INTERVAL_RE, INTERVAL_STRING_RE } from './interval_regexp'; + +describe('interval_regexp', () => { + describe('GTE_INTERVAL_RE', () => { + test('returns true for">=12h"', () => { + const value = GTE_INTERVAL_RE.test('>=12h'); + + expect(value).toBeTruthy(); + }); + test('returns true for ">=1y"', () => { + const value = GTE_INTERVAL_RE.test('>=12h'); + + expect(value).toBeTruthy(); + }); + test('returns true for ">=25m"', () => { + const value = GTE_INTERVAL_RE.test('>=12h'); + + expect(value).toBeTruthy(); + }); + test('returns false "auto"', () => { + const value = GTE_INTERVAL_RE.test('auto'); + + expect(value).toBeFalsy(); + }); + test('returns false "wrongInput"', () => { + const value = GTE_INTERVAL_RE.test('wrongInput'); + + expect(value).toBeFalsy(); + }); + test('returns false "d"', () => { + const value = GTE_INTERVAL_RE.test('d'); + + expect(value).toBeFalsy(); + }); + + test('returns false "y"', () => { + const value = GTE_INTERVAL_RE.test('y'); + + expect(value).toBeFalsy(); + }); + }); + + describe('INTERVAL_STRING_RE', () => { + test('returns true for "8d"', () => { + const value = INTERVAL_STRING_RE.test('8d'); + + expect(value).toBeTruthy(); + }); + test('returns true for "1y"', () => { + const value = INTERVAL_STRING_RE.test('1y'); + + expect(value).toBeTruthy(); + }); + test('returns true for "6M"', () => { + const value = INTERVAL_STRING_RE.test('6M'); + + expect(value).toBeTruthy(); + }); + test('returns false "auto"', () => { + const value = INTERVAL_STRING_RE.test('auto'); + + expect(value).toBeFalsy(); + }); + test('returns false "wrongInput"', () => { + const value = INTERVAL_STRING_RE.test('wrongInput'); + + expect(value).toBeFalsy(); + }); + test('returns false for">=21h"', () => { + const value = INTERVAL_STRING_RE.test('>=21h'); + + expect(value).toBeFalsy(); + }); + }); +}); From c93e17010dbfade21dddf93d87c0ffd3a4cfbd6f Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 7 Mar 2019 11:13:51 +0300 Subject: [PATCH 26/27] TSVB display interval information when building (#32117) --- .../public/components/index_pattern.js | 1 + .../components/lib/get_axis_label_string.js | 36 +++----- .../public/components/lib/get_interval.js | 75 +++++++++++++++ .../components/vis_editor_visualization.js | 92 +++++++++++++++---- .../components/vis_types/timeseries/vis.js | 15 +-- .../translations/translations/zh-CN.json | 14 +-- 6 files changed, 173 insertions(+), 60 deletions(-) create mode 100644 src/legacy/core_plugins/metrics/public/components/lib/get_interval.js diff --git a/src/legacy/core_plugins/metrics/public/components/index_pattern.js b/src/legacy/core_plugins/metrics/public/components/index_pattern.js index ae5321c95337c..82ce177e7dacb 100644 --- a/src/legacy/core_plugins/metrics/public/components/index_pattern.js +++ b/src/legacy/core_plugins/metrics/public/components/index_pattern.js @@ -117,6 +117,7 @@ export const IndexPattern = props => { disabled={props.disabled} onChange={handleTextChange(intervalName, 'auto')} value={model[intervalName]} + placeholder={'auto'} /> diff --git a/src/legacy/core_plugins/metrics/public/components/lib/get_axis_label_string.js b/src/legacy/core_plugins/metrics/public/components/lib/get_axis_label_string.js index c19cfffdc4538..3fbf940fc5c56 100644 --- a/src/legacy/core_plugins/metrics/public/components/lib/get_axis_label_string.js +++ b/src/legacy/core_plugins/metrics/public/components/lib/get_axis_label_string.js @@ -16,32 +16,20 @@ * specific language governing permissions and limitations * under the License. */ - -import { relativeOptions } from '../../../../../ui/public/timepicker/relative_options'; -import _ from 'lodash'; -import moment from 'moment'; +import { convertIntervalIntoUnit } from './get_interval'; import { i18n } from '@kbn/i18n'; -const unitLookup = { - s: i18n.translate('tsvb.axisLabelOptions.secondsLabel', { defaultMessage: 'seconds' }), - m: i18n.translate('tsvb.axisLabelOptions.minutesLabel', { defaultMessage: 'minutes' }), - h: i18n.translate('tsvb.axisLabelOptions.hoursLabel', { defaultMessage: 'hours' }), - d: i18n.translate('tsvb.axisLabelOptions.daysLabel', { defaultMessage: 'days' }), - w: i18n.translate('tsvb.axisLabelOptions.weeksLabel', { defaultMessage: 'weeks' }), - M: i18n.translate('tsvb.axisLabelOptions.monthsLabel', { defaultMessage: 'months' }), - y: i18n.translate('tsvb.axisLabelOptions.yearsLabel', { defaultMessage: 'years' }) -}; export function getAxisLabelString(interval) { - const units = _.pluck(_.clone(relativeOptions).reverse(), 'value') - .filter(s => /^[smhdwMy]$/.test(s)); - const duration = moment.duration(interval, 'ms'); - for (let i = 0; i < units.length; i++) { - const as = duration.as(units[i]); - if (Math.abs(as) > 1) { - const unitValue = Math.round(Math.abs(as)); - const unitString = unitLookup[units[i]]; - return i18n.translate('tsvb.axisLabelOptions.axisLabel', - { defaultMessage: 'per {unitValue} {unitString}', values: { unitValue, unitString } }); - } + const convertedValue = convertIntervalIntoUnit(interval); + + if (convertedValue) { + return i18n.translate('tsvb.axisLabelOptions.axisLabel', + { + defaultMessage: 'per {unitValue} {unitString}', + values: { + unitValue: convertedValue.unitValue, + unitString: convertedValue.unitString, + }, + }); } } diff --git a/src/legacy/core_plugins/metrics/public/components/lib/get_interval.js b/src/legacy/core_plugins/metrics/public/components/lib/get_interval.js new file mode 100644 index 0000000000000..252173a87f1ad --- /dev/null +++ b/src/legacy/core_plugins/metrics/public/components/lib/get_interval.js @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import moment from 'moment'; +import { i18n } from '@kbn/i18n'; +import { pluck, get, clone, isString } from 'lodash'; +import { relativeOptions } from '../../../../../ui/public/timepicker/relative_options'; + +import { GTE_INTERVAL_RE, INTERVAL_STRING_RE } from '../../../common/interval_regexp'; + +export const unitLookup = { + s: i18n.translate('tsvb.getInterval.secondsLabel', { defaultMessage: 'seconds' }), + m: i18n.translate('tsvb.getInterval.minutesLabel', { defaultMessage: 'minutes' }), + h: i18n.translate('tsvb.getInterval.hoursLabel', { defaultMessage: 'hours' }), + d: i18n.translate('tsvb.getInterval.daysLabel', { defaultMessage: 'days' }), + w: i18n.translate('tsvb.getInterval.weeksLabel', { defaultMessage: 'weeks' }), + M: i18n.translate('tsvb.getInterval.monthsLabel', { defaultMessage: 'months' }), + y: i18n.translate('tsvb.getInterval.yearsLabel', { defaultMessage: 'years' }) +}; + +export const convertIntervalIntoUnit = (interval, hasTranslateUnitString = true) => { + const units = pluck(clone(relativeOptions).reverse(), 'value') + .filter(s => /^[smhdwMy]$/.test(s)); + const duration = moment.duration(interval, 'ms'); + + for (let i = 0; i < units.length; i++) { + const as = duration.as(units[i]); + + if (Math.abs(as) > 1) { + return { + unitValue: Math.round(Math.abs(as)), + unitString: hasTranslateUnitString ? unitLookup[units[i]] : units[i] + }; + } + } +}; +export const isGteInterval = (interval) => GTE_INTERVAL_RE.test(interval); + +export const isIntervalValid = (interval) => { + return isString(interval) && + (interval === 'auto' || INTERVAL_STRING_RE.test(interval) || isGteInterval(interval)); +}; + +export const getInterval = (visData, model) => { + let series; + + if (model && model.type === 'table') { + series = get(visData, `series[0].series`, []); + } else { + series = get(visData, `${model.id}.series`, []); + } + + return series.reduce((currentInterval, item) => { + if (item.data.length > 1) { + const seriesInterval = item.data[1][0] - item.data[0][0]; + if (!currentInterval || seriesInterval < currentInterval) return seriesInterval; + } + return currentInterval; + }, 0); +}; diff --git a/src/legacy/core_plugins/metrics/public/components/vis_editor_visualization.js b/src/legacy/core_plugins/metrics/public/components/vis_editor_visualization.js index eac9a9e99b849..c6ba52f34841f 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_editor_visualization.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_editor_visualization.js @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ - import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import { get } from 'lodash'; import { keyCodes, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui'; import { getVisualizeLoader } from 'ui/visualize/loader/visualize_loader'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import { getInterval, convertIntervalIntoUnit, isIntervalValid, isGteInterval } from './lib/get_interval'; const MIN_CHART_HEIGHT = 250; @@ -30,7 +31,8 @@ class VisEditorVisualization extends Component { super(props); this.state = { height: MIN_CHART_HEIGHT, - dragging: false + dragging: false, + panelInterval: 0, }; this.handleMouseUp = this.handleMouseUp.bind(this); @@ -53,7 +55,7 @@ class VisEditorVisualization extends Component { this.handleMouseMove = (event) => { if (this.state.dragging) { this.setState((prevState) => ({ - height: Math.max(MIN_CHART_HEIGHT, prevState.height + event.movementY) + height: Math.max(MIN_CHART_HEIGHT, prevState.height + event.movementY), })); } }; @@ -67,16 +69,16 @@ class VisEditorVisualization extends Component { if (this._handler) { this._handler.destroy(); } - if(this._subscription) { + if (this._subscription) { this._subscription.unsubscribe(); } } onUpdate = () => { this._handler.update({ - timeRange: this.props.timeRange + timeRange: this.props.timeRange, }); - } + }; _loadVisualization() { getVisualizeLoader().then(loader => { @@ -94,6 +96,7 @@ class VisEditorVisualization extends Component { }); this._subscription = this._handler.data$.subscribe((data) => { + this.setPanelInterval(data.visData); this.props.onDataChange(data); }); @@ -103,6 +106,14 @@ class VisEditorVisualization extends Component { }); } + setPanelInterval(visData) { + const panelInterval = getInterval(visData, this.props.model); + + if (this.state.panelInterval !== panelInterval) { + this.setState({ panelInterval }); + } + } + componentDidUpdate() { if (!this._handler) { this._handlerUpdateHasAlreadyBeenTriggered = true; @@ -115,6 +126,7 @@ class VisEditorVisualization extends Component { componentDidMount() { this._loadVisualization(); } + /** * Resize the chart height when pressing up/down while the drag handle * for resizing has the focus. @@ -128,12 +140,40 @@ class VisEditorVisualization extends Component { this.setState((prevState) => { const newHeight = prevState.height + (keyCode === keyCodes.UP ? -15 : 15); return { - height: Math.max(MIN_CHART_HEIGHT, newHeight) + height: Math.max(MIN_CHART_HEIGHT, newHeight), }; }); } } + hasShowPanelIntervalValue() { + const type = get(this.props, 'model.type', ''); + + return [ + 'metric', + 'top_n', + 'gauge', + 'markdown', + 'table', + ].includes(type); + } + + getFormattedPanelInterval() { + const interval = get(this.props, 'model.interval') || 'auto'; + const isValid = isIntervalValid(interval); + const shouldShowActualInterval = interval === 'auto' || isGteInterval(interval); + + if (shouldShowActualInterval || !isValid) { + const autoInterval = convertIntervalIntoUnit(this.state.panelInterval, false); + + if (autoInterval) { + return `${autoInterval.unitValue}${autoInterval.unitString}`; + } + } else { + return interval; + } + } + render() { const { dirty, autoApply } = this.props; const style = { height: this.state.height }; @@ -141,6 +181,8 @@ class VisEditorVisualization extends Component { style.userSelect = 'none'; } + const panelInterval = this.hasShowPanelIntervalValue() && this.getFormattedPanelInterval(); + let applyMessage = ( + {panelInterval && + + +

+ +

+
+
+ } +

@@ -180,14 +238,14 @@ class VisEditorVisualization extends Component { {!autoApply && - - - - - + + + + + } ); @@ -213,10 +271,10 @@ class VisEditorVisualization extends Component { onKeyDown={this.onSizeHandleKeyDown} aria-label={this.props.intl.formatMessage({ id: 'tsvb.colorRules.adjustChartSizeAriaLabel', - defaultMessage: 'Press up/down to adjust the chart size' + defaultMessage: 'Press up/down to adjust the chart size', })} > - +

diff --git a/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/vis.js b/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/vis.js index 14fea06707481..72ea297f7e12d 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/vis.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/vis.js @@ -27,6 +27,7 @@ import _ from 'lodash'; import Timeseries from '../../../visualizations/components/timeseries'; import replaceVars from '../../lib/replace_vars'; import { getAxisLabelString } from '../../lib/get_axis_label_string'; +import { getInterval } from '../../lib/get_interval'; import { createXaxisFormatter } from '../../lib/create_xaxis_formatter'; function hasSeparateAxis(row) { @@ -35,20 +36,10 @@ function hasSeparateAxis(row) { class TimeseriesVisualization extends Component { - constructor(props) { - super(props); - } - getInterval = () => { const { visData, model } = this.props; - const series = _.get(visData, `${model.id}.series`, []); - return series.reduce((currentInterval, item) => { - if (item.data.length > 1) { - const seriesInterval = item.data[1][0] - item.data[0][0]; - if (!currentInterval || seriesInterval < currentInterval) return seriesInterval; - } - return currentInterval; - }, 0); + + return getInterval(visData, model); } xaxisFormatter = (val) => { diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c91eeb08ef7eb..95acd2f14b057 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2808,13 +2808,13 @@ "tsvb.annotationsEditor.rowTemplateLabel": "行模板(必需)", "tsvb.annotationsEditor.timeFieldLabel": "时间字段(必需)", "tsvb.axisLabelOptions.axisLabel": "每 {unitValue} {unitString}", - "tsvb.axisLabelOptions.daysLabel": "天", - "tsvb.axisLabelOptions.hoursLabel": "小时", - "tsvb.axisLabelOptions.minutesLabel": "分钟", - "tsvb.axisLabelOptions.monthsLabel": "个月", - "tsvb.axisLabelOptions.secondsLabel": "秒", - "tsvb.axisLabelOptions.weeksLabel": "周", - "tsvb.axisLabelOptions.yearsLabel": "年", + "tsvb.getInterval.daysLabel": "天", + "tsvb.getInterval.hoursLabel": "小时", + "tsvb.getInterval.minutesLabel": "分钟", + "tsvb.getInterval.monthsLabel": "个月", + "tsvb.getInterval.secondsLabel": "秒", + "tsvb.getInterval.weeksLabel": "周", + "tsvb.getInterval.yearsLabel": "年", "tsvb.calculateLabel.bucketScriptsLabel": "桶脚本", "tsvb.calculateLabel.countLabel": "计数", "tsvb.calculateLabel.filterRatioLabel": "筛选比", From fc06cdded285a5609e1a29a8210b56f71fc64d08 Mon Sep 17 00:00:00 2001 From: rockfield Date: Thu, 7 Mar 2019 12:38:39 +0300 Subject: [PATCH 27/27] since xpack.canvas label is not on the list of translated plugins, i left as it is for awhile --- .../arg_types/series_style/simple_template.js | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js index 4602a8b1b0519..20b92c2fe7964 100644 --- a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js +++ b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.js @@ -9,12 +9,11 @@ import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiButtonIcon } from '@elastic/eui'; import { set, del } from 'object-path-immutable'; import { get } from 'lodash'; -import { injectI18n } from '@kbn/i18n/react'; import { ColorPickerMini } from '../../../components/color_picker_mini'; import { TooltipIcon } from '../../../components/tooltip_icon'; -const SimpleTemplateUI = props => { - const { typeInstance, argValue, onValueChange, labels, workpad, intl } = props; +export const SimpleTemplate = props => { + const { typeInstance, argValue, onValueChange, labels, workpad } = props; const { name } = typeInstance; const chain = get(argValue, 'chain.0', {}); const chainArgs = get(chain, 'arguments', {}); @@ -37,13 +36,7 @@ const SimpleTemplateUI = props => { Color  - handlePlain('color', '#000000')} - > + handlePlain('color', '#000000')}> Auto @@ -85,9 +78,9 @@ const SimpleTemplateUI = props => { ); }; -SimpleTemplateUI.displayName = 'SeriesStyleArgSimpleInput'; +SimpleTemplate.displayName = 'SeriesStyleArgSimpleInput'; -SimpleTemplateUI.propTypes = { +SimpleTemplate.propTypes = { onValueChange: PropTypes.func.isRequired, argValue: PropTypes.any.isRequired, labels: PropTypes.array, @@ -95,7 +88,4 @@ SimpleTemplateUI.propTypes = { colors: PropTypes.array.isRequired, }).isRequired, typeInstance: PropTypes.shape({ name: PropTypes.string.isRequired }).isRequired, - intl: PropTypes.any.isRequired, }; - -export const SimpleTemplate = injectI18n(SimpleTemplateUI);