diff --git a/.github/workflows/pr-project-assigner.yml b/.github/workflows/pr-project-assigner.yml
index aea8a9cad6b1f..e2e5ce1a4d963 100644
--- a/.github/workflows/pr-project-assigner.yml
+++ b/.github/workflows/pr-project-assigner.yml
@@ -11,5 +11,5 @@ jobs:
uses: elastic/github-actions/project-assigner@v1.0.0
id: project_assigner
with:
- issue-mappings: '[{"label": "Team:AppAch", "projectName": "kibana-app-arch", "columnId": 6173897}]'
- ghToken: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ issue-mappings: '[{"label": "Team:AppArch", "projectName": "kibana-app-arch", "columnId": 6173897}, {"label": "Feature:Lens", "projectName": "Lens", "columnId": 6219362}]'
+ ghToken: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/project-assigner.yml b/.github/workflows/project-assigner.yml
index c7f17993249eb..6fd6fd645b5ee 100644
--- a/.github/workflows/project-assigner.yml
+++ b/.github/workflows/project-assigner.yml
@@ -11,7 +11,7 @@ jobs:
uses: elastic/github-actions/project-assigner@v1.0.0
id: project_assigner
with:
- issue-mappings: '[{"label": "Team:AppArch", "projectName": "kibana-app-arch", "columnId": 6173895}]'
+ issue-mappings: '[{"label": "Team:AppArch", "projectName": "kibana-app-arch", "columnId": 6173895}, {"label": "Feature:Lens", "projectName": "Lens", "columnId": 6219363}]'
ghToken: ${{ secrets.GITHUB_TOKEN }}
diff --git a/package.json b/package.json
index a771a130d08b1..1cec7aaacbbc9 100644
--- a/package.json
+++ b/package.json
@@ -116,7 +116,7 @@
"@elastic/charts": "^14.0.0",
"@elastic/datemath": "5.0.2",
"@elastic/ems-client": "1.0.5",
- "@elastic/eui": "17.0.0",
+ "@elastic/eui": "17.1.2",
"@elastic/filesaver": "1.1.2",
"@elastic/good": "8.1.1-kibana2",
"@elastic/numeral": "2.3.3",
diff --git a/packages/kbn-analytics/scripts/build.js b/packages/kbn-analytics/scripts/build.js
index b7fbe629246ec..bb28c1460c9c2 100644
--- a/packages/kbn-analytics/scripts/build.js
+++ b/packages/kbn-analytics/scripts/build.js
@@ -55,7 +55,9 @@ run(
'--extensions',
'.ts,.js,.tsx',
...(flags.watch ? ['--watch'] : ['--quiet']),
- ...(flags['source-maps'] ? ['--source-maps', 'inline'] : []),
+ ...(!flags['source-maps'] || !!process.env.CODE_COVERAGE
+ ? []
+ : ['--source-maps', 'inline']),
],
wait: true,
env: {
diff --git a/packages/kbn-i18n/scripts/build.js b/packages/kbn-i18n/scripts/build.js
index ccdddc87dbc18..0764451c74575 100644
--- a/packages/kbn-i18n/scripts/build.js
+++ b/packages/kbn-i18n/scripts/build.js
@@ -55,7 +55,9 @@ run(
'--extensions',
'.ts,.js,.tsx',
...(flags.watch ? ['--watch'] : ['--quiet']),
- ...(flags['source-maps'] ? ['--source-maps', 'inline'] : []),
+ ...(!flags['source-maps'] || !!process.env.CODE_COVERAGE
+ ? []
+ : ['--source-maps', 'inline']),
],
wait: true,
env: {
diff --git a/packages/kbn-utility-types/index.ts b/packages/kbn-utility-types/index.ts
index 495b5fb374b43..36bbc8cc82873 100644
--- a/packages/kbn-utility-types/index.ts
+++ b/packages/kbn-utility-types/index.ts
@@ -18,6 +18,7 @@
*/
import { PromiseType } from 'utility-types';
+export { $Values, Required, Optional, Class } from 'utility-types';
/**
* Returns wrapped type of a promise.
diff --git a/packages/kbn-utility-types/package.json b/packages/kbn-utility-types/package.json
index a79d08677020b..a999eb41eb781 100644
--- a/packages/kbn-utility-types/package.json
+++ b/packages/kbn-utility-types/package.json
@@ -13,7 +13,7 @@
"clean": "del target"
},
"dependencies": {
- "utility-types": "^3.7.0"
+ "utility-types": "^3.10.0"
},
"devDependencies": {
"del-cli": "^3.0.0",
diff --git a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap
index 626c91b6a9668..9bd686776138f 100644
--- a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap
+++ b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap
@@ -31,7 +31,7 @@ Array [
]
`;
-exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"
"`;
+exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `""`;
exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 1`] = `
Array [
@@ -74,4 +74,4 @@ Array [
]
`;
-exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `""`;
+exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `""`;
diff --git a/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap b/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap
index 3928c54f90179..131ec836f5252 100644
--- a/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap
+++ b/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap
@@ -29,7 +29,7 @@ Array [
]
`;
-exports[`ModalService openModal() renders a modal to the DOM 2`] = `"
"`;
+exports[`ModalService openModal() renders a modal to the DOM 2`] = `"
"`;
exports[`ModalService openModal() with a currently active modal replaces the current modal with a new one 1`] = `
Array [
diff --git a/src/legacy/core_plugins/console/public/np_ready/application/models/legacy_core_editor/mode/script_highlight_rules.js b/src/legacy/core_plugins/console/public/np_ready/application/models/legacy_core_editor/mode/script_highlight_rules.js
index 801580f4e158c..b3999c76493c0 100644
--- a/src/legacy/core_plugins/console/public/np_ready/application/models/legacy_core_editor/mode/script_highlight_rules.js
+++ b/src/legacy/core_plugins/console/public/np_ready/application/models/legacy_core_editor/mode/script_highlight_rules.js
@@ -61,7 +61,6 @@ export function ScriptHighlightRules() {
},
{
token: 'script.keyword.operator',
-
regex:
'\\?\\.|\\*\\.|=~|==~|!|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|->|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|typeof|void)',
},
diff --git a/src/legacy/core_plugins/input_control_vis/index.ts b/src/legacy/core_plugins/input_control_vis/index.ts
new file mode 100644
index 0000000000000..8f6178e26126b
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/index.ts
@@ -0,0 +1,44 @@
+/*
+ * 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 { resolve } from 'path';
+import { Legacy } from 'kibana';
+
+import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types';
+
+const inputControlVisPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
+ new Plugin({
+ id: 'input_control_vis',
+ require: ['kibana', 'elasticsearch', 'visualizations', 'interpreter', 'data'],
+ publicDir: resolve(__dirname, 'public'),
+ uiExports: {
+ styleSheetPaths: resolve(__dirname, 'public/index.scss'),
+ hacks: [resolve(__dirname, 'public/legacy')],
+ injectDefaultVars: server => ({}),
+ },
+ init: (server: Legacy.Server) => ({}),
+ config(Joi: any) {
+ return Joi.object({
+ enabled: Joi.boolean().default(true),
+ }).default();
+ },
+ } as Legacy.PluginSpecOptions);
+
+// eslint-disable-next-line import/no-default-export
+export default inputControlVisPluginInitializer;
diff --git a/src/legacy/core_plugins/input_control_vis/public/__snapshots__/input_control_fn.test.js.snap b/src/legacy/core_plugins/input_control_vis/public/__snapshots__/input_control_fn.test.ts.snap
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/__snapshots__/input_control_fn.test.js.snap
rename to src/legacy/core_plugins/input_control_vis/public/__snapshots__/input_control_fn.test.ts.snap
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.js.snap b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap
similarity index 73%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.js.snap
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap
index 809214f756713..632fe63e9e148 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.js.snap
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap
@@ -16,9 +16,33 @@ exports[`renders ControlsTab 1`] = `
"size": 5,
"type": "terms",
},
+ "parent": "parent",
"type": "list",
}
}
+ deps={
+ Object {
+ "core": Object {
+ "getStartServices": [MockFunction],
+ "injectedMetadata": Object {
+ "getInjectedVar": [MockFunction],
+ },
+ },
+ "data": Object {
+ "query": Object {
+ "filterManager": Object {
+ "fieldName": "myField",
+ "getAppFilters": [MockFunction],
+ "getGlobalFilters": [MockFunction],
+ "getIndexPattern": [Function],
+ },
+ "timefilter": Object {
+ "timefilter": Object {},
+ },
+ },
+ },
+ }
+ }
getIndexPattern={[Function]}
handleCheckboxOptionChange={[Function]}
handleFieldNameChange={[Function]}
@@ -49,9 +73,33 @@ exports[`renders ControlsTab 1`] = `
"options": Object {
"step": 1,
},
+ "parent": "parent",
"type": "range",
}
}
+ deps={
+ Object {
+ "core": Object {
+ "getStartServices": [MockFunction],
+ "injectedMetadata": Object {
+ "getInjectedVar": [MockFunction],
+ },
+ },
+ "data": Object {
+ "query": Object {
+ "filterManager": Object {
+ "fieldName": "myField",
+ "getAppFilters": [MockFunction],
+ "getGlobalFilters": [MockFunction],
+ "getIndexPattern": [Function],
+ },
+ "timefilter": Object {
+ "timefilter": Object {},
+ },
+ },
+ },
+ }
+ }
getIndexPattern={[Function]}
handleCheckboxOptionChange={[Function]}
handleFieldNameChange={[Function]}
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.js.snap b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.tsx.snap
similarity index 98%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.js.snap
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.tsx.snap
index ff3d1ffc146e3..9bc8b1b9ac5cd 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.js.snap
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.tsx.snap
@@ -3,6 +3,7 @@
exports[`renders dynamic options should display disabled dynamic options with tooltip for non-string fields 1`] = `
{
+ return fields.find(({ name: n }) => n === name);
+};
+
+export const getDepsMock = (): InputControlVisDependencies =>
+ ({
+ core: {
+ getStartServices: jest.fn().mockReturnValue([
+ null,
+ {
+ data: {
+ ui: {
+ IndexPatternSelect: () => (
) as any,
+ },
+ indexPatterns: {
+ get: () => ({
+ fields,
+ }),
+ },
+ },
+ },
+ ]),
+ injectedMetadata: {
+ getInjectedVar: jest.fn().mockImplementation(key => {
+ switch (key) {
+ case 'autocompleteTimeout':
+ return 1000;
+ case 'autocompleteTerminateAfter':
+ return 100000;
+ default:
+ return '';
+ }
+ }),
+ },
+ },
+ data: {
+ query: {
+ filterManager: {
+ fieldName: 'myField',
+ getIndexPattern: () => ({
+ fields,
+ }),
+ getAppFilters: jest.fn().mockImplementation(() => []),
+ getGlobalFilters: jest.fn().mockImplementation(() => []),
+ },
+ timefilter: {
+ timefilter: {},
+ },
+ },
+ },
+ } as any);
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_pattern_mock.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_pattern_mock.ts
similarity index 82%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_pattern_mock.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_pattern_mock.ts
index c693bf100e265..638dd7170cb8d 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_pattern_mock.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_pattern_mock.ts
@@ -17,7 +17,12 @@
* under the License.
*/
-export const getIndexPatternMock = () => {
+import { IIndexPattern } from '../../../../../../../plugins/data/public';
+
+/**
+ * Returns forced **Partial** IndexPattern for use in tests
+ */
+export const getIndexPatternMock = (): Promise => {
return Promise.resolve({
id: 'mockIndexPattern',
title: 'mockIndexPattern',
@@ -26,5 +31,5 @@ export const getIndexPatternMock = () => {
{ name: 'textField', type: 'string', aggregatable: false },
{ name: 'numberField', type: 'number', aggregatable: true },
],
- });
+ } as IIndexPattern);
};
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_patterns_mock.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_patterns_mock.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_patterns_mock.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_index_patterns_mock.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_search_service_mock.ts b/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_search_service_mock.ts
new file mode 100644
index 0000000000000..9da47bedcc784
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/get_search_service_mock.ts
@@ -0,0 +1,46 @@
+/*
+ * 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 { SearchSource } from '../../../legacy_imports';
+
+export const getSearchSourceMock = (esSearchResponse?: any): SearchSource =>
+ jest.fn().mockImplementation(() => ({
+ setParent: jest.fn(),
+ setField: jest.fn(),
+ fetch: jest.fn().mockResolvedValue(
+ esSearchResponse
+ ? esSearchResponse
+ : {
+ aggregations: {
+ termsAgg: {
+ buckets: [
+ {
+ key: 'Zurich Airport',
+ doc_count: 691,
+ },
+ {
+ key: 'Xi an Xianyang International Airport',
+ doc_count: 526,
+ },
+ ],
+ },
+ },
+ }
+ ),
+ }));
diff --git a/src/legacy/core_plugins/input_control_vis/index.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/update_component.ts
similarity index 65%
rename from src/legacy/core_plugins/input_control_vis/index.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/update_component.ts
index a21b79e28fb7f..881412a7c56fd 100644
--- a/src/legacy/core_plugins/input_control_vis/index.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__tests__/update_component.ts
@@ -17,14 +17,15 @@
* under the License.
*/
-import { resolve } from 'path';
+import { ShallowWrapper, ReactWrapper } from 'enzyme';
-export default function(kibana) {
- return new kibana.Plugin({
- uiExports: {
- visTypes: ['plugins/input_control_vis/register_vis'],
- interpreter: ['plugins/input_control_vis/input_control_fn'],
- styleSheetPaths: resolve(__dirname, 'public/index.scss'),
- },
- });
-}
+export const updateComponent = async (
+ component:
+ | ShallowWrapper, React.Component<{}, {}, any>>
+ | ReactWrapper, React.Component<{}, {}, any>>
+) => {
+ // Ensure all promises resolve
+ await new Promise(resolve => process.nextTick(resolve));
+ // Ensure the state changes are reflected
+ component.update();
+};
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/control_editor.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/control_editor.tsx
similarity index 72%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/control_editor.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/control_editor.tsx
index cc31b8d238dbe..dbac5d9d94371 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/control_editor.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/control_editor.tsx
@@ -17,13 +17,10 @@
* under the License.
*/
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
-import { RangeControlEditor } from './range_control_editor';
-import { ListControlEditor } from './list_control_editor';
-import { getTitle } from '../../editor_utils';
-import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
+import React, { PureComponent, ChangeEvent } from 'react';
+import { InjectedIntlProps } from 'react-intl';
+import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
import {
EuiAccordion,
EuiButtonIcon,
@@ -32,11 +29,45 @@ import {
EuiFormRow,
EuiPanel,
EuiSpacer,
+ EuiSwitchEvent,
} from '@elastic/eui';
-class ControlEditorUi extends Component {
- changeLabel = evt => {
- this.props.handleLabelChange(this.props.controlIndex, evt);
+import { RangeControlEditor } from './range_control_editor';
+import { ListControlEditor } from './list_control_editor';
+import { getTitle, ControlParams, CONTROL_TYPES, ControlParamsOptions } from '../../editor_utils';
+import { IIndexPattern } from '../../../../../../plugins/data/public';
+import { InputControlVisDependencies } from '../../plugin';
+
+interface ControlEditorUiProps {
+ controlIndex: number;
+ controlParams: ControlParams;
+ handleLabelChange: (controlIndex: number, event: ChangeEvent) => void;
+ moveControl: (controlIndex: number, direction: number) => void;
+ handleRemoveControl: (controlIndex: number) => void;
+ handleIndexPatternChange: (controlIndex: number, indexPatternId: string) => void;
+ handleFieldNameChange: (controlIndex: number, fieldName: string) => void;
+ getIndexPattern: (indexPatternId: string) => Promise;
+ handleCheckboxOptionChange: (
+ controlIndex: number,
+ optionName: keyof ControlParamsOptions,
+ event: EuiSwitchEvent
+ ) => void;
+ handleNumberOptionChange: (
+ controlIndex: number,
+ optionName: keyof ControlParamsOptions,
+ event: ChangeEvent
+ ) => void;
+ parentCandidates: Array<{
+ value: string;
+ text: string;
+ }>;
+ handleParentChange: (controlIndex: number, event: ChangeEvent) => void;
+ deps: InputControlVisDependencies;
+}
+
+class ControlEditorUi extends PureComponent {
+ changeLabel = (event: ChangeEvent) => {
+ this.props.handleLabelChange(this.props.controlIndex, event);
};
removeControl = () => {
@@ -51,18 +82,18 @@ class ControlEditorUi extends Component {
this.props.moveControl(this.props.controlIndex, 1);
};
- changeIndexPattern = evt => {
- this.props.handleIndexPatternChange(this.props.controlIndex, evt);
+ changeIndexPattern = (indexPatternId: string) => {
+ this.props.handleIndexPatternChange(this.props.controlIndex, indexPatternId);
};
- changeFieldName = evt => {
- this.props.handleFieldNameChange(this.props.controlIndex, evt);
+ changeFieldName = (fieldName: string) => {
+ this.props.handleFieldNameChange(this.props.controlIndex, fieldName);
};
renderEditor() {
let controlEditor = null;
switch (this.props.controlParams.type) {
- case 'list':
+ case CONTROL_TYPES.LIST:
controlEditor = (
);
break;
- case 'range':
+ case CONTROL_TYPES.RANGE:
controlEditor = (
);
break;
@@ -167,24 +200,4 @@ class ControlEditorUi extends Component {
}
}
-ControlEditorUi.propTypes = {
- controlIndex: PropTypes.number.isRequired,
- controlParams: PropTypes.object.isRequired,
- handleLabelChange: PropTypes.func.isRequired,
- moveControl: PropTypes.func.isRequired,
- handleRemoveControl: PropTypes.func.isRequired,
- handleIndexPatternChange: PropTypes.func.isRequired,
- handleFieldNameChange: PropTypes.func.isRequired,
- getIndexPattern: PropTypes.func.isRequired,
- handleCheckboxOptionChange: PropTypes.func.isRequired,
- handleNumberOptionChange: PropTypes.func.isRequired,
- parentCandidates: PropTypes.arrayOf(
- PropTypes.shape({
- value: PropTypes.string.isRequired,
- text: PropTypes.string.isRequired,
- })
- ).isRequired,
- handleParentChange: PropTypes.func.isRequired,
-};
-
export const ControlEditor = injectI18n(ControlEditorUi);
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
similarity index 87%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
index 28f435c27ea8f..4e7c9bafbf510 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
@@ -17,45 +17,36 @@
* under the License.
*/
-jest.mock('../../../../../core_plugins/data/public/legacy', () => ({
- indexPatterns: {
- indexPatterns: {
- get: jest.fn(),
- },
- },
-}));
-
-jest.mock('ui/new_platform', () => ({
- npStart: {
- plugins: {
- data: {
- ui: {
- IndexPatternSelect: () => {
- return
;
- },
- },
- },
- },
- },
-}));
-
import React from 'react';
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
+// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
+import { getDepsMock } from './__tests__/get_deps_mock';
import { getIndexPatternMock } from './__tests__/get_index_pattern_mock';
-import { ControlsTab } from './controls_tab';
+import { ControlsTab, ControlsTabUiProps } from './controls_tab';
const indexPatternsMock = {
get: getIndexPatternMock,
};
-let props;
+let props: ControlsTabUiProps;
beforeEach(() => {
props = {
+ deps: getDepsMock(),
vis: {
API: {
indexPatterns: indexPatternsMock,
},
+ type: {
+ name: 'test',
+ title: 'test',
+ visualization: null,
+ requestHandler: 'test',
+ responseHandler: 'test',
+ stage: 'beta',
+ requiresSearch: false,
+ hidden: false,
+ },
},
stateParams: {
controls: [
@@ -71,6 +62,7 @@ beforeEach(() => {
size: 5,
order: 'desc',
},
+ parent: 'parent',
},
{
id: '2',
@@ -81,10 +73,12 @@ beforeEach(() => {
options: {
step: 1,
},
+ parent: 'parent',
},
],
},
setValue: jest.fn(),
+ intl: null as any,
};
});
@@ -105,7 +99,7 @@ describe('behavior', () => {
'controls',
expect.arrayContaining(props.stateParams.controls)
);
- expect(props.setValue.mock.calls[0][1].length).toEqual(3);
+ expect((props.setValue as jest.Mock).mock.calls[0][1].length).toEqual(3);
});
test('remove control button', () => {
@@ -120,6 +114,7 @@ describe('behavior', () => {
fieldName: 'numberField',
label: '',
type: 'range',
+ parent: 'parent',
options: {
step: 1,
},
@@ -142,6 +137,7 @@ describe('behavior', () => {
fieldName: 'numberField',
label: '',
type: 'range',
+ parent: 'parent',
options: {
step: 1,
},
@@ -152,6 +148,7 @@ describe('behavior', () => {
fieldName: 'keywordField',
label: 'custom label',
type: 'list',
+ parent: 'parent',
options: {
type: 'terms',
multiselect: true,
@@ -177,6 +174,7 @@ describe('behavior', () => {
fieldName: 'numberField',
label: '',
type: 'range',
+ parent: 'parent',
options: {
step: 1,
},
@@ -187,6 +185,7 @@ describe('behavior', () => {
fieldName: 'keywordField',
label: 'custom label',
type: 'list',
+ parent: 'parent',
options: {
type: 'terms',
multiselect: true,
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.tsx
similarity index 68%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.tsx
index 97036d7b0f5df..56381ef7d1570 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.tsx
@@ -17,14 +17,10 @@
* under the License.
*/
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
-import { ControlEditor } from './control_editor';
-import { addControl, moveControl, newControl, removeControl, setControl } from '../../editor_utils';
-import { getLineageMap, getParentCandidates } from '../../lineage';
-import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
-import { npStart } from 'ui/new_platform';
+import React, { PureComponent, ChangeEvent } from 'react';
+import { InjectedIntlProps } from 'react-intl';
+import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
import {
EuiButton,
EuiFlexGroup,
@@ -32,55 +28,97 @@ import {
EuiFormRow,
EuiPanel,
EuiSelect,
+ EuiSwitchEvent,
} from '@elastic/eui';
-class ControlsTabUi extends Component {
+import { ControlEditor } from './control_editor';
+import {
+ addControl,
+ moveControl,
+ newControl,
+ removeControl,
+ setControl,
+ ControlParams,
+ CONTROL_TYPES,
+ ControlParamsOptions,
+} from '../../editor_utils';
+import { getLineageMap, getParentCandidates } from '../../lineage';
+import { IIndexPattern } from '../../../../../../plugins/data/public';
+import { VisOptionsProps } from '../../legacy_imports';
+import { InputControlVisDependencies } from '../../plugin';
+
+interface ControlsTabUiState {
+ type: CONTROL_TYPES;
+}
+
+interface ControlsTabUiParams {
+ controls: ControlParams[];
+}
+type ControlsTabUiInjectedProps = InjectedIntlProps &
+ Pick, 'vis' | 'stateParams' | 'setValue'> & {
+ deps: InputControlVisDependencies;
+ };
+
+export type ControlsTabUiProps = ControlsTabUiInjectedProps;
+
+class ControlsTabUi extends PureComponent {
state = {
- type: 'list',
+ type: CONTROL_TYPES.LIST,
};
- getIndexPattern = async indexPatternId => {
- return await npStart.plugins.data.indexPatterns.get(indexPatternId);
+ getIndexPattern = async (indexPatternId: string): Promise => {
+ const [, startDeps] = await this.props.deps.core.getStartServices();
+ return await startDeps.data.indexPatterns.get(indexPatternId);
};
- onChange = value => this.props.setValue('controls', value);
+ onChange = (value: ControlParams[]) => this.props.setValue('controls', value);
- handleLabelChange = (controlIndex, evt) => {
+ handleLabelChange = (controlIndex: number, event: ChangeEvent) => {
const updatedControl = this.props.stateParams.controls[controlIndex];
- updatedControl.label = evt.target.value;
+ updatedControl.label = event.target.value;
this.onChange(setControl(this.props.stateParams.controls, controlIndex, updatedControl));
};
- handleIndexPatternChange = (controlIndex, indexPatternId) => {
+ handleIndexPatternChange = (controlIndex: number, indexPatternId: string) => {
const updatedControl = this.props.stateParams.controls[controlIndex];
updatedControl.indexPattern = indexPatternId;
updatedControl.fieldName = '';
this.onChange(setControl(this.props.stateParams.controls, controlIndex, updatedControl));
};
- handleFieldNameChange = (controlIndex, fieldName) => {
+ handleFieldNameChange = (controlIndex: number, fieldName: string) => {
const updatedControl = this.props.stateParams.controls[controlIndex];
updatedControl.fieldName = fieldName;
this.onChange(setControl(this.props.stateParams.controls, controlIndex, updatedControl));
};
- handleCheckboxOptionChange = (controlIndex, optionName, evt) => {
+ handleCheckboxOptionChange = (
+ controlIndex: number,
+ optionName: keyof ControlParamsOptions,
+ event: EuiSwitchEvent
+ ) => {
const updatedControl = this.props.stateParams.controls[controlIndex];
- updatedControl.options[optionName] = evt.target.checked;
+ // @ts-ignore
+ updatedControl.options[optionName] = event.target.checked;
this.onChange(setControl(this.props.stateParams.controls, controlIndex, updatedControl));
};
- handleNumberOptionChange = (controlIndex, optionName, evt) => {
+ handleNumberOptionChange = (
+ controlIndex: number,
+ optionName: keyof ControlParamsOptions,
+ event: ChangeEvent
+ ) => {
const updatedControl = this.props.stateParams.controls[controlIndex];
- updatedControl.options[optionName] = parseFloat(evt.target.value);
+ // @ts-ignore
+ updatedControl.options[optionName] = parseFloat(event.target.value);
this.onChange(setControl(this.props.stateParams.controls, controlIndex, updatedControl));
};
- handleRemoveControl = controlIndex => {
+ handleRemoveControl = (controlIndex: number) => {
this.onChange(removeControl(this.props.stateParams.controls, controlIndex));
};
- moveControl = (controlIndex, direction) => {
+ moveControl = (controlIndex: number, direction: number) => {
this.onChange(moveControl(this.props.stateParams.controls, controlIndex, direction));
};
@@ -88,9 +126,9 @@ class ControlsTabUi extends Component {
this.onChange(addControl(this.props.stateParams.controls, newControl(this.state.type)));
};
- handleParentChange = (controlIndex, evt) => {
+ handleParentChange = (controlIndex: number, event: ChangeEvent) => {
const updatedControl = this.props.stateParams.controls[controlIndex];
- updatedControl.parent = evt.target.value;
+ updatedControl.parent = event.target.value;
this.onChange(setControl(this.props.stateParams.controls, controlIndex, updatedControl));
};
@@ -117,6 +155,7 @@ class ControlsTabUi extends Component {
handleNumberOptionChange={this.handleNumberOptionChange}
parentCandidates={parentCandidates}
handleParentChange={this.handleParentChange}
+ deps={this.props.deps}
/>
);
});
@@ -137,14 +176,14 @@ class ControlsTabUi extends Component {
data-test-subj="selectControlType"
options={[
{
- value: 'range',
+ value: CONTROL_TYPES.RANGE,
text: intl.formatMessage({
id: 'inputControl.editor.controlsTab.select.rangeDropDownOptionLabel',
defaultMessage: 'Range slider',
}),
},
{
- value: 'list',
+ value: CONTROL_TYPES.LIST,
text: intl.formatMessage({
id: 'inputControl.editor.controlsTab.select.listDropDownOptionLabel',
defaultMessage: 'Options list',
@@ -152,7 +191,7 @@ class ControlsTabUi extends Component {
},
]}
value={this.state.type}
- onChange={evt => this.setState({ type: evt.target.value })}
+ onChange={event => this.setState({ type: event.target.value as CONTROL_TYPES })}
aria-label={intl.formatMessage({
id: 'inputControl.editor.controlsTab.select.controlTypeAriaLabel',
defaultMessage: 'Select control type',
@@ -186,9 +225,8 @@ class ControlsTabUi extends Component {
}
}
-ControlsTabUi.propTypes = {
- vis: PropTypes.object.isRequired,
- setValue: PropTypes.func.isRequired,
-};
-
export const ControlsTab = injectI18n(ControlsTabUi);
+
+export const getControlsTab = (deps: InputControlVisDependencies) => (
+ props: Omit
+) => ;
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/field_select.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/field_select.tsx
similarity index 70%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/field_select.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/field_select.tsx
index 456ff17a316a1..bde2f09ab0a47 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/field_select.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/field_select.tsx
@@ -18,43 +18,59 @@
*/
import _ from 'lodash';
-import PropTypes from 'prop-types';
import React, { Component } from 'react';
+import { InjectedIntlProps } from 'react-intl';
+
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
+import { EuiFormRow, EuiComboBox, EuiComboBoxOptionProps } from '@elastic/eui';
+
+import { IIndexPattern, IFieldType } from '../../../../../../plugins/data/public';
+
+interface FieldSelectUiState {
+ isLoading: boolean;
+ fields: Array>;
+ indexPatternId: string;
+}
+
+export type FieldSelectUiProps = InjectedIntlProps & {
+ getIndexPattern: (indexPatternId: string) => Promise;
+ indexPatternId: string;
+ onChange: (value: any) => void;
+ fieldName?: string;
+ filterField?: (field: IFieldType) => boolean;
+ controlIndex: number;
+};
-import { EuiFormRow, EuiComboBox } from '@elastic/eui';
+class FieldSelectUi extends Component {
+ private hasUnmounted: boolean;
-class FieldSelectUi extends Component {
- constructor(props) {
+ constructor(props: FieldSelectUiProps) {
super(props);
- this._hasUnmounted = false;
+ this.hasUnmounted = false;
this.state = {
isLoading: false,
fields: [],
indexPatternId: props.indexPatternId,
};
- this.filterField = _.get(props, 'filterField', () => {
- return true;
- });
}
componentWillUnmount() {
- this._hasUnmounted = true;
+ this.hasUnmounted = true;
}
componentDidMount() {
this.loadFields(this.state.indexPatternId);
}
- UNSAFE_componentWillReceiveProps(nextProps) {
+ UNSAFE_componentWillReceiveProps(nextProps: FieldSelectUiProps) {
if (this.props.indexPatternId !== nextProps.indexPatternId) {
- this.loadFields(nextProps.indexPatternId);
+ this.loadFields(nextProps.indexPatternId ?? '');
}
}
- loadFields = indexPatternId => {
+ loadFields = (indexPatternId: string) => {
this.setState(
{
isLoading: true,
@@ -65,12 +81,12 @@ class FieldSelectUi extends Component {
);
};
- debouncedLoad = _.debounce(async indexPatternId => {
+ debouncedLoad = _.debounce(async (indexPatternId: string) => {
if (!indexPatternId || indexPatternId.length === 0) {
return;
}
- let indexPattern;
+ let indexPattern: IIndexPattern;
try {
indexPattern = await this.props.getIndexPattern(indexPatternId);
} catch (err) {
@@ -78,7 +94,7 @@ class FieldSelectUi extends Component {
return;
}
- if (this._hasUnmounted) {
+ if (this.hasUnmounted) {
return;
}
@@ -88,17 +104,15 @@ class FieldSelectUi extends Component {
return;
}
- const fieldsByTypeMap = new Map();
- const fields = [];
- indexPattern.fields.filter(this.filterField).forEach(field => {
- if (fieldsByTypeMap.has(field.type)) {
- const fieldsList = fieldsByTypeMap.get(field.type);
+ const fieldsByTypeMap = new Map();
+ const fields: Array> = [];
+ indexPattern.fields
+ .filter(this.props.filterField ?? (() => true))
+ .forEach((field: IFieldType) => {
+ const fieldsList = fieldsByTypeMap.get(field.type) ?? [];
fieldsList.push(field.name);
fieldsByTypeMap.set(field.type, fieldsList);
- } else {
- fieldsByTypeMap.set(field.type, [field.name]);
- }
- });
+ });
fieldsByTypeMap.forEach((fieldsList, fieldType) => {
fields.push({
@@ -117,11 +131,11 @@ class FieldSelectUi extends Component {
this.setState({
isLoading: false,
- fields: fields,
+ fields,
});
}, 300);
- onChange = selectedOptions => {
+ onChange = (selectedOptions: Array>) => {
this.props.onChange(_.get(selectedOptions, '0.value'));
};
@@ -165,13 +179,4 @@ class FieldSelectUi extends Component {
}
}
-FieldSelectUi.propTypes = {
- getIndexPattern: PropTypes.func.isRequired,
- indexPatternId: PropTypes.string,
- onChange: PropTypes.func.isRequired,
- fieldName: PropTypes.string,
- filterField: PropTypes.func,
- controlIndex: PropTypes.number.isRequired,
-};
-
export const FieldSelect = injectI18n(FieldSelectUi);
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.tsx
similarity index 73%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.tsx
index 7d7fbc0539de0..66fdbca64f053 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.tsx
@@ -17,15 +17,20 @@
* under the License.
*/
-import PropTypes from 'prop-types';
-import React from 'react';
+import React, { ComponentType } from 'react';
import { injectI18n } from '@kbn/i18n/react';
import { EuiFormRow } from '@elastic/eui';
+import { InjectedIntlProps } from 'react-intl';
+import { IndexPatternSelect } from 'src/plugins/data/public';
-import { npStart } from 'ui/new_platform';
-const { IndexPatternSelect } = npStart.plugins.data.ui;
+export type IndexPatternSelectFormRowUiProps = InjectedIntlProps & {
+ onChange: (opt: any) => void;
+ indexPatternId: string;
+ controlIndex: number;
+ IndexPatternSelect: ComponentType;
+};
-function IndexPatternSelectFormRowUi(props) {
+function IndexPatternSelectFormRowUi(props: IndexPatternSelectFormRowUiProps) {
const { controlIndex, indexPatternId, intl, onChange } = props;
const selectId = `indexPatternSelect-${controlIndex}`;
@@ -37,7 +42,7 @@ function IndexPatternSelectFormRowUi(props) {
defaultMessage: 'Index Pattern',
})}
>
-
);
}
-IndexPatternSelectFormRowUi.propTypes = {
- onChange: PropTypes.func.isRequired,
- indexPatternId: PropTypes.string,
- controlIndex: PropTypes.number.isRequired,
-};
-
export const IndexPatternSelectFormRow = injectI18n(IndexPatternSelectFormRowUi);
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.tsx
similarity index 80%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.tsx
index 24b14943c8bb3..de0187f87212f 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.test.tsx
@@ -17,31 +17,21 @@
* under the License.
*/
-jest.mock('ui/new_platform', () => ({
- npStart: {
- plugins: {
- data: {
- ui: {
- IndexPatternSelect: () => {
- return
;
- },
- },
- },
- },
- },
-}));
-
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
-import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
+// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
-import { getIndexPatternMock } from './__tests__/get_index_pattern_mock';
+import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
+import { getDepsMock } from './__tests__/get_deps_mock';
+import { getIndexPatternMock } from './__tests__/get_index_pattern_mock';
import { ListControlEditor } from './list_control_editor';
+import { ControlParams } from '../../editor_utils';
+import { updateComponent } from './__tests__/update_component';
-const controlParams = {
+const controlParamsBase: ControlParams = {
id: '1',
indexPattern: 'indexPattern1',
fieldName: 'keywordField',
@@ -53,11 +43,13 @@ const controlParams = {
dynamicOptions: false,
size: 10,
},
+ parent: '',
};
-let handleFieldNameChange;
-let handleIndexPatternChange;
-let handleCheckboxOptionChange;
-let handleNumberOptionChange;
+const deps = getDepsMock();
+let handleFieldNameChange: sinon.SinonSpy;
+let handleIndexPatternChange: sinon.SinonSpy;
+let handleCheckboxOptionChange: sinon.SinonSpy;
+let handleNumberOptionChange: sinon.SinonSpy;
beforeEach(() => {
handleFieldNameChange = sinon.spy();
@@ -68,8 +60,9 @@ beforeEach(() => {
describe('renders', () => {
test('should not display any options until field is selected', async () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
+ label: 'mock',
indexPattern: 'mockIndexPattern',
fieldName: '',
type: 'list',
@@ -79,9 +72,11 @@ describe('renders', () => {
dynamicOptions: true,
size: 5,
},
+ parent: '',
};
const component = shallow(
{
/>
);
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
+ await updateComponent(component);
expect(component).toMatchSnapshot();
});
@@ -109,9 +101,10 @@ describe('renders', () => {
];
const component = shallow(
{
/>
);
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
+ await updateComponent(component);
expect(component).toMatchSnapshot();
});
describe('dynamic options', () => {
test('should display dynamic options for string fields', async () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
+ label: 'mock',
indexPattern: 'mockIndexPattern',
fieldName: 'keywordField',
type: 'list',
@@ -142,9 +133,11 @@ describe('renders', () => {
dynamicOptions: true,
size: 5,
},
+ parent: '',
};
const component = shallow(
{
/>
);
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
+ await updateComponent(component);
expect(component).toMatchSnapshot();
});
test('should display size field when dynamic options is disabled', async () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
+ label: 'mock',
indexPattern: 'mockIndexPattern',
fieldName: 'keywordField',
type: 'list',
@@ -177,9 +168,11 @@ describe('renders', () => {
dynamicOptions: false,
size: 5,
},
+ parent: '',
};
const component = shallow(
{
/>
);
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
+ await updateComponent(component);
expect(component).toMatchSnapshot();
});
test('should display disabled dynamic options with tooltip for non-string fields', async () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
+ label: 'mock',
indexPattern: 'mockIndexPattern',
fieldName: 'numberField',
type: 'list',
@@ -212,9 +203,11 @@ describe('renders', () => {
dynamicOptions: true,
size: 5,
},
+ parent: '',
};
const component = shallow(
{
/>
);
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
+ await updateComponent(component);
expect(component).toMatchSnapshot();
});
@@ -240,9 +230,10 @@ describe('renders', () => {
test('handleCheckboxOptionChange - multiselect', async () => {
const component = mountWithIntl(
{
/>
);
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
+ await updateComponent(component);
const checkbox = findTestSubject(component, 'listControlMultiselectInput');
checkbox.simulate('click');
@@ -268,10 +256,10 @@ test('handleCheckboxOptionChange - multiselect', async () => {
handleCheckboxOptionChange,
expectedControlIndex,
expectedOptionName,
- sinon.match(evt => {
- // Synthetic `evt.target.checked` does not get altered by EuiSwitch,
+ sinon.match(event => {
+ // Synthetic `event.target.checked` does not get altered by EuiSwitch,
// but its aria attribute is correctly updated
- if (evt.target.getAttribute('aria-checked') === 'true') {
+ if (event.target.getAttribute('aria-checked') === 'true') {
return true;
}
return false;
@@ -282,9 +270,10 @@ test('handleCheckboxOptionChange - multiselect', async () => {
test('handleNumberOptionChange - size', async () => {
const component = mountWithIntl(
{
/>
);
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
+ await updateComponent(component);
const input = findTestSubject(component, 'listControlSizeInput');
input.simulate('change', { target: { value: 7 } });
@@ -310,8 +296,8 @@ test('handleNumberOptionChange - size', async () => {
handleNumberOptionChange,
expectedControlIndex,
expectedOptionName,
- sinon.match(evt => {
- if (evt.target.value === 7) {
+ sinon.match(event => {
+ if (event.target.value === 7) {
return true;
}
return false;
@@ -322,9 +308,10 @@ test('handleNumberOptionChange - size', async () => {
test('field name change', async () => {
const component = shallowWithIntl(
{
/>
);
- const update = async () => {
- // Ensure all promises resolve
- await new Promise(resolve => process.nextTick(resolve));
- // Ensure the state changes are reflected
- component.update();
- };
-
// ensure that after async loading is complete the DynamicOptionsSwitch is not disabled
expect(
component.find('[data-test-subj="listControlDynamicOptionsSwitch"][disabled=false]')
).toHaveLength(0);
- await update();
+ await updateComponent(component);
expect(
component.find('[data-test-subj="listControlDynamicOptionsSwitch"][disabled=false]')
).toHaveLength(1);
component.setProps({
controlParams: {
- ...controlParams,
+ ...controlParamsBase,
fieldName: 'numberField',
},
});
@@ -361,20 +341,20 @@ test('field name change', async () => {
expect(
component.find('[data-test-subj="listControlDynamicOptionsSwitch"][disabled=true]')
).toHaveLength(0);
- await update();
+ await updateComponent(component);
expect(
component.find('[data-test-subj="listControlDynamicOptionsSwitch"][disabled=true]')
).toHaveLength(1);
component.setProps({
- controlParams,
+ controlParams: controlParamsBase,
});
// ensure that after async loading is complete the DynamicOptionsSwitch is not disabled again, because we switched to original "string" field
expect(
component.find('[data-test-subj="listControlDynamicOptionsSwitch"][disabled=false]')
).toHaveLength(0);
- await update();
+ await updateComponent(component);
expect(
component.find('[data-test-subj="listControlDynamicOptionsSwitch"][disabled=false]')
).toHaveLength(1);
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.tsx
similarity index 70%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.tsx
index 2ee225475b0fe..ff74d30a6e1a8 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/list_control_editor.tsx
@@ -17,35 +17,90 @@
* under the License.
*/
-import PropTypes from 'prop-types';
-import React, { Component, Fragment } from 'react';
+import React, { PureComponent, ChangeEvent, ComponentType } from 'react';
+
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+ EuiFormRow,
+ EuiFieldNumber,
+ EuiSwitch,
+ EuiSelect,
+ EuiSelectProps,
+ EuiSwitchEvent,
+} from '@elastic/eui';
+
import { IndexPatternSelectFormRow } from './index_pattern_select_form_row';
import { FieldSelect } from './field_select';
-import { FormattedMessage } from '@kbn/i18n/react';
+import { ControlParams, ControlParamsOptions } from '../../editor_utils';
+import {
+ IIndexPattern,
+ IFieldType,
+ IndexPatternSelect,
+} from '../../../../../../plugins/data/public';
+import { InputControlVisDependencies } from '../../plugin';
-import { EuiFormRow, EuiFieldNumber, EuiSwitch, EuiSelect } from '@elastic/eui';
+interface ListControlEditorState {
+ isLoadingFieldType: boolean;
+ isStringField: boolean;
+ prevFieldName: string;
+ IndexPatternSelect: ComponentType | null;
+}
-function filterField(field) {
- return field.aggregatable && ['number', 'boolean', 'date', 'ip', 'string'].includes(field.type);
+interface ListControlEditorProps {
+ getIndexPattern: (indexPatternId: string) => Promise;
+ controlIndex: number;
+ controlParams: ControlParams;
+ handleFieldNameChange: (fieldName: string) => void;
+ handleIndexPatternChange: (indexPatternId: string) => void;
+ handleCheckboxOptionChange: (
+ controlIndex: number,
+ optionName: keyof ControlParamsOptions,
+ event: EuiSwitchEvent
+ ) => void;
+ handleNumberOptionChange: (
+ controlIndex: number,
+ optionName: keyof ControlParamsOptions,
+ event: ChangeEvent
+ ) => void;
+ handleParentChange: (controlIndex: number, event: ChangeEvent) => void;
+ parentCandidates: EuiSelectProps['options'];
+ deps: InputControlVisDependencies;
}
-export class ListControlEditor extends Component {
- state = {
+function filterField(field: IFieldType) {
+ return (
+ Boolean(field.aggregatable) &&
+ ['number', 'boolean', 'date', 'ip', 'string'].includes(field.type)
+ );
+}
+
+export class ListControlEditor extends PureComponent<
+ ListControlEditorProps,
+ ListControlEditorState
+> {
+ private isMounted: boolean = false;
+
+ state: ListControlEditorState = {
isLoadingFieldType: true,
isStringField: false,
prevFieldName: this.props.controlParams.fieldName,
+ IndexPatternSelect: null,
};
componentDidMount() {
- this._isMounted = true;
+ this.isMounted = true;
this.loadIsStringField();
+ this.getIndexPatternSelect();
}
componentWillUnmount() {
- this._isMounted = false;
+ this.isMounted = false;
}
- static getDerivedStateFromProps = (nextProps, prevState) => {
+ static getDerivedStateFromProps = (
+ nextProps: ListControlEditorProps,
+ prevState: ListControlEditorState
+ ) => {
const isNewFieldName = prevState.prevFieldName !== nextProps.controlParams.fieldName;
if (!prevState.isLoadingFieldType && isNewFieldName) {
return {
@@ -63,13 +118,20 @@ export class ListControlEditor extends Component {
}
};
+ async getIndexPatternSelect() {
+ const [, { data }] = await this.props.deps.core.getStartServices();
+ this.setState({
+ IndexPatternSelect: data.ui.IndexPatternSelect,
+ });
+ }
+
loadIsStringField = async () => {
if (!this.props.controlParams.indexPattern || !this.props.controlParams.fieldName) {
this.setState({ isLoadingFieldType: false });
return;
}
- let indexPattern;
+ let indexPattern: IIndexPattern;
try {
indexPattern = await this.props.getIndexPattern(this.props.controlParams.indexPattern);
} catch (err) {
@@ -77,13 +139,13 @@ export class ListControlEditor extends Component {
return;
}
- if (!this._isMounted) {
+ if (!this.isMounted) {
return;
}
- const field = indexPattern.fields.find(field => {
- return field.name === this.props.controlParams.fieldName;
- });
+ const field = (indexPattern.fields as IFieldType[]).find(
+ ({ name }) => name === this.props.controlParams.fieldName
+ );
if (!field) {
return;
}
@@ -121,8 +183,8 @@ export class ListControlEditor extends Component {
{
- this.props.handleParentChange(this.props.controlIndex, evt);
+ onChange={event => {
+ this.props.handleParentChange(this.props.controlIndex, event);
}}
/>
@@ -147,9 +209,9 @@ export class ListControlEditor extends Component {
defaultMessage="Multiselect"
/>
}
- checked={this.props.controlParams.options.multiselect}
- onChange={evt => {
- this.props.handleCheckboxOptionChange(this.props.controlIndex, 'multiselect', evt);
+ checked={this.props.controlParams.options.multiselect ?? true}
+ onChange={event => {
+ this.props.handleCheckboxOptionChange(this.props.controlIndex, 'multiselect', event);
}}
data-test-subj="listControlMultiselectInput"
/>
@@ -180,9 +242,9 @@ export class ListControlEditor extends Component {
defaultMessage="Dynamic Options"
/>
}
- checked={this.props.controlParams.options.dynamicOptions}
- onChange={evt => {
- this.props.handleCheckboxOptionChange(this.props.controlIndex, 'dynamicOptions', evt);
+ checked={this.props.controlParams.options.dynamicOptions ?? false}
+ onChange={event => {
+ this.props.handleCheckboxOptionChange(this.props.controlIndex, 'dynamicOptions', event);
}}
disabled={this.state.isStringField ? false : true}
data-test-subj="listControlDynamicOptionsSwitch"
@@ -212,8 +274,8 @@ export class ListControlEditor extends Component {
{
- this.props.handleNumberOptionChange(this.props.controlIndex, 'size', evt);
+ onChange={event => {
+ this.props.handleNumberOptionChange(this.props.controlIndex, 'size', event);
}}
data-test-subj="listControlSizeInput"
/>
@@ -225,12 +287,17 @@ export class ListControlEditor extends Component {
};
render() {
+ if (this.state.IndexPatternSelect === null) {
+ return null;
+ }
+
return (
-
+ <>
{this.renderOptions()}
-
+ >
);
}
}
-
-ListControlEditor.propTypes = {
- getIndexPattern: PropTypes.func.isRequired,
- controlIndex: PropTypes.number.isRequired,
- controlParams: PropTypes.object.isRequired,
- handleFieldNameChange: PropTypes.func.isRequired,
- handleIndexPatternChange: PropTypes.func.isRequired,
- handleCheckboxOptionChange: PropTypes.func.isRequired,
- handleNumberOptionChange: PropTypes.func.isRequired,
- parentCandidates: PropTypes.arrayOf(
- PropTypes.shape({
- value: PropTypes.string.isRequired,
- text: PropTypes.string.isRequired,
- })
- ).isRequired,
- handleParentChange: PropTypes.func.isRequired,
-};
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.tsx
similarity index 93%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.tsx
index ef84d37ca8de5..36ec4d4446fd6 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.test.tsx
@@ -21,17 +21,19 @@ import React from 'react';
import { shallow } from 'enzyme';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-import { OptionsTab } from './options_tab';
+import { OptionsTab, OptionsTabProps } from './options_tab';
+import { Vis } from '../../legacy_imports';
describe('OptionsTab', () => {
- let props;
+ let props: OptionsTabProps;
beforeEach(() => {
props = {
- vis: {},
+ vis: {} as Vis,
stateParams: {
updateFiltersOnChange: false,
useTimeFilter: false,
+ pinFilters: false,
},
setValue: jest.fn(),
};
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.tsx
similarity index 74%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.tsx
index 236624b11118c..43f9e15302e51 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.tsx
@@ -17,24 +17,37 @@
* under the License.
*/
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
import { EuiForm, EuiFormRow, EuiSwitch } from '@elastic/eui';
-
import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiSwitchEvent } from '@elastic/eui';
+
+import { VisOptionsProps } from '../../legacy_imports';
+
+interface OptionsTabParams {
+ updateFiltersOnChange: boolean;
+ useTimeFilter: boolean;
+ pinFilters: boolean;
+}
+type OptionsTabInjectedProps = Pick<
+ VisOptionsProps,
+ 'vis' | 'setValue' | 'stateParams'
+>;
+
+export type OptionsTabProps = OptionsTabInjectedProps;
-export class OptionsTab extends Component {
- handleUpdateFiltersChange = evt => {
- this.props.setValue('updateFiltersOnChange', evt.target.checked);
+export class OptionsTab extends PureComponent {
+ handleUpdateFiltersChange = (event: EuiSwitchEvent) => {
+ this.props.setValue('updateFiltersOnChange', event.target.checked);
};
- handleUseTimeFilter = evt => {
- this.props.setValue('useTimeFilter', evt.target.checked);
+ handleUseTimeFilter = (event: EuiSwitchEvent) => {
+ this.props.setValue('useTimeFilter', event.target.checked);
};
- handlePinFilters = evt => {
- this.props.setValue('pinFilters', evt.target.checked);
+ handlePinFilters = (event: EuiSwitchEvent) => {
+ this.props.setValue('pinFilters', event.target.checked);
};
render() {
@@ -85,8 +98,3 @@ export class OptionsTab extends Component {
);
}
}
-
-OptionsTab.propTypes = {
- vis: PropTypes.object.isRequired,
- setValue: PropTypes.func.isRequired,
-};
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.js
deleted file mode 100644
index 6e1754b28647f..0000000000000
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.js
+++ /dev/null
@@ -1,102 +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 PropTypes from 'prop-types';
-import React, { Fragment } from 'react';
-import { IndexPatternSelectFormRow } from './index_pattern_select_form_row';
-import { FieldSelect } from './field_select';
-
-import { EuiFormRow, EuiFieldNumber } from '@elastic/eui';
-
-import { FormattedMessage } from '@kbn/i18n/react';
-
-function filterField(field) {
- return field.type === 'number';
-}
-
-export function RangeControlEditor(props) {
- const stepSizeId = `stepSize-${props.controlIndex}`;
- const decimalPlacesId = `decimalPlaces-${props.controlIndex}`;
- const handleDecimalPlacesChange = evt => {
- props.handleNumberOptionChange(props.controlIndex, 'decimalPlaces', evt);
- };
- const handleStepChange = evt => {
- props.handleNumberOptionChange(props.controlIndex, 'step', evt);
- };
- return (
-
-
-
-
-
-
- }
- >
-
-
-
-
- }
- >
-
-
-
- );
-}
-
-RangeControlEditor.propTypes = {
- getIndexPattern: PropTypes.func.isRequired,
- controlIndex: PropTypes.number.isRequired,
- controlParams: PropTypes.object.isRequired,
- handleFieldNameChange: PropTypes.func.isRequired,
- handleIndexPatternChange: PropTypes.func.isRequired,
- handleNumberOptionChange: PropTypes.func.isRequired,
-};
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.js b/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx
similarity index 71%
rename from src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.js
rename to src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx
index 145b18a42dc15..e7f9b6083890c 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.test.tsx
@@ -18,30 +18,20 @@
*/
import React from 'react';
-import sinon from 'sinon';
import { shallow } from 'enzyme';
+import { SinonSpy, spy, assert, match } from 'sinon';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-jest.mock('ui/new_platform', () => ({
- npStart: {
- plugins: {
- data: {
- ui: {
- IndexPatternSelect: () => {
- return
;
- },
- },
- },
- },
- },
-}));
-
+// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { getIndexPatternMock } from './__tests__/get_index_pattern_mock';
import { RangeControlEditor } from './range_control_editor';
+import { ControlParams } from '../../editor_utils';
+import { getDepsMock } from './__tests__/get_deps_mock';
+import { updateComponent } from './__tests__/update_component';
-const controlParams = {
+const controlParams: ControlParams = {
id: '1',
indexPattern: 'indexPattern1',
fieldName: 'numberField',
@@ -51,20 +41,23 @@ const controlParams = {
decimalPlaces: 0,
step: 1,
},
+ parent: '',
};
-let handleFieldNameChange;
-let handleIndexPatternChange;
-let handleNumberOptionChange;
+const deps = getDepsMock();
+let handleFieldNameChange: SinonSpy;
+let handleIndexPatternChange: SinonSpy;
+let handleNumberOptionChange: SinonSpy;
beforeEach(() => {
- handleFieldNameChange = sinon.spy();
- handleIndexPatternChange = sinon.spy();
- handleNumberOptionChange = sinon.spy();
+ handleFieldNameChange = spy();
+ handleIndexPatternChange = spy();
+ handleNumberOptionChange = spy();
});
-test('renders RangeControlEditor', () => {
+test('renders RangeControlEditor', async () => {
const component = shallow(
{
handleNumberOptionChange={handleNumberOptionChange}
/>
);
+
+ await updateComponent(component);
+
expect(component).toMatchSnapshot(); // eslint-disable-line
});
-test('handleNumberOptionChange - step', () => {
+test('handleNumberOptionChange - step', async () => {
const component = mountWithIntl(
{
handleNumberOptionChange={handleNumberOptionChange}
/>
);
+
+ await updateComponent(component);
+
findTestSubject(component, 'rangeControlSizeInput0').simulate('change', {
target: { value: 0.5 },
});
- sinon.assert.notCalled(handleFieldNameChange);
- sinon.assert.notCalled(handleIndexPatternChange);
+ assert.notCalled(handleFieldNameChange);
+ assert.notCalled(handleIndexPatternChange);
const expectedControlIndex = 0;
const expectedOptionName = 'step';
- sinon.assert.calledWith(
+ assert.calledWith(
handleNumberOptionChange,
expectedControlIndex,
expectedOptionName,
- sinon.match(evt => {
- if (evt.target.value === 0.5) {
+ match(event => {
+ if (event.target.value === 0.5) {
return true;
}
return false;
@@ -107,9 +107,10 @@ test('handleNumberOptionChange - step', () => {
);
});
-test('handleNumberOptionChange - decimalPlaces', () => {
+test('handleNumberOptionChange - decimalPlaces', async () => {
const component = mountWithIntl(
{
handleNumberOptionChange={handleNumberOptionChange}
/>
);
+
+ await updateComponent(component);
+
findTestSubject(component, 'rangeControlDecimalPlacesInput0').simulate('change', {
target: { value: 2 },
});
- sinon.assert.notCalled(handleFieldNameChange);
- sinon.assert.notCalled(handleIndexPatternChange);
+ assert.notCalled(handleFieldNameChange);
+ assert.notCalled(handleIndexPatternChange);
const expectedControlIndex = 0;
const expectedOptionName = 'decimalPlaces';
- sinon.assert.calledWith(
+ assert.calledWith(
handleNumberOptionChange,
expectedControlIndex,
expectedOptionName,
- sinon.match(evt => {
- if (evt.target.value === 2) {
+ match(event => {
+ if (event.target.value === 2) {
return true;
}
return false;
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.tsx b/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.tsx
new file mode 100644
index 0000000000000..44477eafda6b1
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/range_control_editor.tsx
@@ -0,0 +1,139 @@
+/*
+ * 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 React, { Component, Fragment, ChangeEvent, ComponentType } from 'react';
+
+import { EuiFormRow, EuiFieldNumber } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+
+import { IndexPatternSelectFormRow } from './index_pattern_select_form_row';
+import { FieldSelect } from './field_select';
+import { ControlParams, ControlParamsOptions } from '../../editor_utils';
+import {
+ IIndexPattern,
+ IFieldType,
+ IndexPatternSelect,
+} from '../../../../../../plugins/data/public';
+import { InputControlVisDependencies } from '../../plugin';
+
+interface RangeControlEditorProps {
+ controlIndex: number;
+ controlParams: ControlParams;
+ getIndexPattern: (indexPatternId: string) => Promise;
+ handleFieldNameChange: (fieldName: string) => void;
+ handleIndexPatternChange: (indexPatternId: string) => void;
+ handleNumberOptionChange: (
+ controlIndex: number,
+ optionName: keyof ControlParamsOptions,
+ event: ChangeEvent
+ ) => void;
+ deps: InputControlVisDependencies;
+}
+
+interface RangeControlEditorState {
+ IndexPatternSelect: ComponentType | null;
+}
+
+function filterField(field: IFieldType) {
+ return field.type === 'number';
+}
+
+export class RangeControlEditor extends Component<
+ RangeControlEditorProps,
+ RangeControlEditorState
+> {
+ state: RangeControlEditorState = {
+ IndexPatternSelect: null,
+ };
+
+ componentDidMount() {
+ this.getIndexPatternSelect();
+ }
+
+ async getIndexPatternSelect() {
+ const [, { data }] = await this.props.deps.core.getStartServices();
+ this.setState({
+ IndexPatternSelect: data.ui.IndexPatternSelect,
+ });
+ }
+
+ render() {
+ const stepSizeId = `stepSize-${this.props.controlIndex}`;
+ const decimalPlacesId = `decimalPlaces-${this.props.controlIndex}`;
+ if (this.state.IndexPatternSelect === null) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+ }
+ >
+ {
+ this.props.handleNumberOptionChange(this.props.controlIndex, 'step', event);
+ }}
+ data-test-subj={`rangeControlSizeInput${this.props.controlIndex}`}
+ />
+
+
+
+ }
+ >
+ {
+ this.props.handleNumberOptionChange(this.props.controlIndex, 'decimalPlaces', event);
+ }}
+ data-test-subj={`rangeControlDecimalPlacesInput${this.props.controlIndex}`}
+ />
+
+
+ );
+ }
+}
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.js.snap b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.tsx.snap
similarity index 98%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.js.snap
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.tsx.snap
index 6437cb19aef97..ba183cc40b126 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.js.snap
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/form_row.test.tsx.snap
@@ -47,7 +47,6 @@ exports[`renders disabled control with tooltip 1`] = `
anchorClassName="eui-displayBlock"
content="I am disabled for testing purposes"
delay="regular"
- placement="top"
position="top"
>
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.js.snap b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.tsx.snap
similarity index 99%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.js.snap
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.tsx.snap
index 841421474e7b1..5a76967c71fbb 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.js.snap
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/input_control_vis.test.tsx.snap
@@ -18,7 +18,6 @@ exports[`Apply and Cancel change btns enabled when there are changes 1`] = `
>
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.test.js b/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.test.js
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.test.tsx
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.js b/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.tsx
similarity index 75%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.js
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.tsx
index fdd4fcb6e26ae..29385582924e7 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/form_row.tsx
@@ -17,16 +17,24 @@
* under the License.
*/
-import PropTypes from 'prop-types';
-import React from 'react';
+import React, { ReactElement } from 'react';
import { EuiFormRow, EuiToolTip, EuiIcon } from '@elastic/eui';
-export function FormRow(props) {
+export interface FormRowProps {
+ label: string;
+ warningMsg?: string;
+ id: string;
+ children: ReactElement;
+ controlIndex: number;
+ disableMsg?: string;
+}
+
+export function FormRow(props: FormRowProps) {
let control = props.children;
if (props.disableMsg) {
control = (
-
+
{control}
);
@@ -49,12 +57,3 @@ export function FormRow(props) {
);
}
-
-FormRow.propTypes = {
- label: PropTypes.string.isRequired,
- warningMsg: PropTypes.string,
- id: PropTypes.string.isRequired,
- children: PropTypes.node.isRequired,
- controlIndex: PropTypes.number.isRequired,
- disableMsg: PropTypes.string,
-};
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.js b/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
similarity index 87%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.js
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
index a835078ab4dc0..1712f024f5b7b 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.tsx
@@ -21,11 +21,16 @@ import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
+// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { InputControlVis } from './input_control_vis';
+import { ListControl } from '../../control/list_control_factory';
+import { RangeControl } from '../../control/range_control_factory';
-const mockListControl = {
+jest.mock('ui/new_platform');
+
+const mockListControl: ListControl = {
id: 'mock-list-control',
isEnabled: () => {
return true;
@@ -38,11 +43,9 @@ const mockListControl = {
label: 'list control',
value: [],
selectOptions: ['choice1', 'choice2'],
- format: value => {
- return value;
- },
-};
-const mockRangeControl = {
+ format: (value: any) => value,
+} as ListControl;
+const mockRangeControl: RangeControl = {
id: 'mock-range-control',
isEnabled: () => {
return true;
@@ -56,16 +59,16 @@ const mockRangeControl = {
value: { min: 0, max: 0 },
min: 0,
max: 100,
- format: value => {
- return value;
- },
-};
+ format: (value: any) => value,
+} as RangeControl;
const updateFiltersOnChange = false;
-let stageFilter;
-let submitFilters;
-let resetControls;
-let clearControls;
+const refreshControlMock = () => Promise.resolve();
+
+let stageFilter: sinon.SinonSpy;
+let submitFilters: sinon.SinonSpy;
+let resetControls: sinon.SinonSpy;
+let clearControls: sinon.SinonSpy;
beforeEach(() => {
stageFilter = sinon.spy();
@@ -89,7 +92,7 @@ test('Renders list control', () => {
hasValues={() => {
return false;
}}
- refreshControl={() => {}}
+ refreshControl={refreshControlMock}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -110,7 +113,7 @@ test('Renders range control', () => {
hasValues={() => {
return false;
}}
- refreshControl={() => {}}
+ refreshControl={refreshControlMock}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -131,7 +134,7 @@ test('Apply and Cancel change btns enabled when there are changes', () => {
hasValues={() => {
return false;
}}
- refreshControl={() => {}}
+ refreshControl={refreshControlMock}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -152,7 +155,7 @@ test('Clear btns enabled when there are values', () => {
hasValues={() => {
return true;
}}
- refreshControl={() => {}}
+ refreshControl={refreshControlMock}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -173,7 +176,7 @@ test('clearControls', () => {
hasValues={() => {
return true;
}}
- refreshControl={() => {}}
+ refreshControl={refreshControlMock}
/>
);
findTestSubject(component, 'inputControlClearBtn').simulate('click');
@@ -198,7 +201,7 @@ test('submitFilters', () => {
hasValues={() => {
return true;
}}
- refreshControl={() => {}}
+ refreshControl={refreshControlMock}
/>
);
findTestSubject(component, 'inputControlSubmitBtn').simulate('click');
@@ -223,7 +226,7 @@ test('resetControls', () => {
hasValues={() => {
return true;
}}
- refreshControl={() => {}}
+ refreshControl={refreshControlMock}
/>
);
findTestSubject(component, 'inputControlCancelBtn').simulate('click');
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.js b/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.tsx
similarity index 60%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.js
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.tsx
index 9e140155698f0..e2497287f35d0 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/input_control_vis.tsx
@@ -17,16 +17,37 @@
* under the License.
*/
-import PropTypes from 'prop-types';
import React, { Component } from 'react';
-import { RangeControl } from './range_control';
-import { ListControl } from './list_control';
import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-
import { FormattedMessage } from '@kbn/i18n/react';
+import { CONTROL_TYPES } from '../../editor_utils';
+import { ListControl } from '../../control/list_control_factory';
+import { RangeControl } from '../../control/range_control_factory';
+import { ListControl as ListControlComponent } from '../vis/list_control';
+import { RangeControl as RangeControlComponent } from '../vis/range_control';
+
+function isListControl(control: RangeControl | ListControl): control is ListControl {
+ return control.type === CONTROL_TYPES.LIST;
+}
+
+function isRangeControl(control: RangeControl | ListControl): control is RangeControl {
+ return control.type === CONTROL_TYPES.RANGE;
+}
+
+interface InputControlVisProps {
+ stageFilter: (controlIndex: number, newValue: any) => void;
+ submitFilters: () => void;
+ resetControls: () => void;
+ clearControls: () => void;
+ controls: Array;
+ updateFiltersOnChange?: boolean;
+ hasChanges: () => boolean;
+ hasValues: () => boolean;
+ refreshControl: (controlIndex: number, query: any) => Promise;
+}
-export class InputControlVis extends Component {
- constructor(props) {
+export class InputControlVis extends Component {
+ constructor(props: InputControlVisProps) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
@@ -49,39 +70,38 @@ export class InputControlVis extends Component {
renderControls() {
return this.props.controls.map((control, index) => {
let controlComponent = null;
- switch (control.type) {
- case 'list':
- controlComponent = (
- {
- this.props.refreshControl(index, query);
- }}
- />
- );
- break;
- case 'range':
- controlComponent = (
-
- );
- break;
- default:
- throw new Error(`Unhandled control type ${control.type}`);
+
+ if (isListControl(control)) {
+ controlComponent = (
+ {
+ this.props.refreshControl(index, query);
+ }}
+ />
+ );
+ } else if (isRangeControl(control)) {
+ controlComponent = (
+
+ );
+ } else {
+ throw new Error(`Unhandled control type ${control!.type}`);
}
+
return (
{
+const formatOptionLabel = (value: any) => {
return `${value} + formatting`;
};
-let stageFilter;
+let stageFilter: sinon.SinonSpy;
beforeEach(() => {
stageFilter = sinon.spy();
@@ -46,6 +46,7 @@ test('renders ListControl', () => {
controlIndex={0}
stageFilter={stageFilter}
formatOptionLabel={formatOptionLabel}
+ intl={{} as any}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
@@ -56,11 +57,13 @@ test('disableMsg', () => {
);
expect(component).toMatchSnapshot(); // eslint-disable-line
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.js b/src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.tsx
similarity index 76%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.js
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.tsx
index 7e92545e817e0..d62adfdce56b4 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/list_control.tsx
@@ -17,46 +17,76 @@
* under the License.
*/
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
import _ from 'lodash';
-import { FormRow } from './form_row';
import { injectI18n } from '@kbn/i18n/react';
+import { InjectedIntlProps } from 'react-intl';
import { EuiFieldText, EuiComboBox } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import { FormRow } from './form_row';
+
+interface ListControlUiState {
+ isLoading: boolean;
+}
+
+export type ListControlUiProps = InjectedIntlProps & {
+ id: string;
+ label: string;
+ selectedOptions: any[];
+ options?: any[];
+ formatOptionLabel: (option: any) => any;
+ disableMsg?: string;
+ multiselect?: boolean;
+ dynamicOptions?: boolean;
+ partialResults?: boolean;
+ controlIndex: number;
+ stageFilter: (controlIndex: number, value: any) => void;
+ fetchOptions?: (searchValue: string) => void;
+};
+
+class ListControlUi extends PureComponent {
+ static defaultProps = {
+ dynamicOptions: false,
+ multiselect: true,
+ selectedOptions: [],
+ options: [],
+ };
+
+ private isMounted: boolean = false;
-class ListControlUi extends Component {
state = {
isLoading: false,
};
componentDidMount = () => {
- this._isMounted = true;
+ this.isMounted = true;
};
componentWillUnmount = () => {
- this._isMounted = false;
+ this.isMounted = false;
};
- handleOnChange = selectedOptions => {
+ handleOnChange = (selectedOptions: any[]) => {
const selectedValues = selectedOptions.map(({ value }) => {
return value;
});
this.props.stageFilter(this.props.controlIndex, selectedValues);
};
- debouncedFetch = _.debounce(async searchValue => {
- await this.props.fetchOptions(searchValue);
+ debouncedFetch = _.debounce(async (searchValue: string) => {
+ if (this.props.fetchOptions) {
+ await this.props.fetchOptions(searchValue);
+ }
- if (this._isMounted) {
+ if (this.isMounted) {
this.setState({
isLoading: false,
});
}
}, 300);
- onSearchChange = searchValue => {
+ onSearchChange = (searchValue: string) => {
this.setState(
{
isLoading: true,
@@ -81,7 +111,7 @@ class ListControlUi extends Component {
}
const options = this.props.options
- .map(option => {
+ ?.map(option => {
return {
label: this.props.formatOptionLabel(option).toString(),
value: option,
@@ -141,29 +171,4 @@ class ListControlUi extends Component {
}
}
-ListControlUi.propTypes = {
- id: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- selectedOptions: PropTypes.array.isRequired,
- options: PropTypes.array,
- formatOptionLabel: PropTypes.func.isRequired,
- disableMsg: PropTypes.string,
- multiselect: PropTypes.bool,
- dynamicOptions: PropTypes.bool,
- partialResults: PropTypes.bool,
- controlIndex: PropTypes.number.isRequired,
- stageFilter: PropTypes.func.isRequired,
- fetchOptions: PropTypes.func,
-};
-
-ListControlUi.defaultProps = {
- dynamicOptions: false,
- multiselect: true,
-};
-
-ListControlUi.defaultProps = {
- selectedOptions: [],
- options: [],
-};
-
export const ListControl = injectI18n(ListControlUi);
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.js b/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.tsx
similarity index 89%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.js
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.tsx
index 8b72def2f2698..639616151a395 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.test.tsx
@@ -21,8 +21,11 @@ import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { RangeControl, ceilWithPrecision, floorWithPrecision } from './range_control';
+import { RangeControl as RangeControlClass } from '../../control/range_control_factory';
-const control = {
+jest.mock('ui/new_platform');
+
+const control: RangeControlClass = {
id: 'mock-range-control',
isEnabled: () => {
return true;
@@ -39,7 +42,7 @@ const control = {
hasValue: () => {
return false;
},
-};
+} as RangeControlClass;
test('renders RangeControl', () => {
const component = shallowWithIntl(
@@ -49,7 +52,7 @@ test('renders RangeControl', () => {
});
test('disabled', () => {
- const disabledRangeControl = {
+ const disabledRangeControl: RangeControlClass = {
id: 'mock-range-control',
isEnabled: () => {
return false;
@@ -64,7 +67,7 @@ test('disabled', () => {
hasValue: () => {
return false;
},
- };
+ } as RangeControlClass;
const component = shallowWithIntl(
{}} />
);
diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.js b/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx
similarity index 72%
rename from src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.js
rename to src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx
index ee3e3c8fe4788..cd3982afd9afd 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.js
+++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx
@@ -18,12 +18,17 @@
*/
import _ from 'lodash';
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
+
+import { ValidatedDualRange } from '../../legacy_imports';
import { FormRow } from './form_row';
-import { ValidatedDualRange } from 'ui/validated_range';
+import { RangeControl as RangeControlClass } from '../../control/range_control_factory';
-function roundWithPrecision(value, decimalPlaces, roundFunction) {
+function roundWithPrecision(
+ value: number,
+ decimalPlaces: number,
+ roundFunction: (n: number) => number
+) {
if (decimalPlaces <= 0) {
return roundFunction(value);
}
@@ -35,18 +40,29 @@ function roundWithPrecision(value, decimalPlaces, roundFunction) {
return results;
}
-export function ceilWithPrecision(value, decimalPlaces) {
+export function ceilWithPrecision(value: number, decimalPlaces: number) {
return roundWithPrecision(value, decimalPlaces, Math.ceil);
}
-export function floorWithPrecision(value, decimalPlaces) {
+export function floorWithPrecision(value: number, decimalPlaces: number) {
return roundWithPrecision(value, decimalPlaces, Math.floor);
}
-export class RangeControl extends Component {
- state = {};
+export interface RangeControlState {
+ value?: [string, string];
+ prevValue?: [string, string];
+}
+
+export interface RangeControlProps {
+ control: RangeControlClass;
+ controlIndex: number;
+ stageFilter: (controlIndex: number, value: any) => void;
+}
+
+export class RangeControl extends PureComponent {
+ state: RangeControlState = {};
- static getDerivedStateFromProps(nextProps, prevState) {
+ static getDerivedStateFromProps(nextProps: RangeControlProps, prevState: RangeControlState) {
const nextValue = nextProps.control.hasValue()
? [nextProps.control.value.min, nextProps.control.value.max]
: ['', ''];
@@ -68,7 +84,7 @@ export class RangeControl extends Component {
return null;
}
- onChangeComplete = _.debounce(value => {
+ onChangeComplete = _.debounce((value: [string, string]) => {
const controlValue = {
min: value[0],
max: value[1],
@@ -111,16 +127,10 @@ export class RangeControl extends Component {
id={this.props.control.id}
label={this.props.control.label}
controlIndex={this.props.controlIndex}
- disableMsg={this.props.control.isEnabled() ? null : this.props.control.disabledReason}
+ disableMsg={this.props.control.isEnabled() ? undefined : this.props.control.disabledReason}
>
{this.renderControl()}
);
}
}
-
-RangeControl.propTypes = {
- control: PropTypes.object.isRequired,
- controlIndex: PropTypes.number.isRequired,
- stageFilter: PropTypes.func.isRequired,
-};
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control.test.js b/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts
similarity index 78%
rename from src/legacy/core_plugins/input_control_vis/public/control/control.test.js
rename to src/legacy/core_plugins/input_control_vis/public/control/control.test.ts
index aa9bed44d031d..e76b199a0262c 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/control.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/control.test.ts
@@ -19,34 +19,50 @@
import expect from '@kbn/expect';
import { Control } from './control';
+import { ControlParams } from '../editor_utils';
+import { FilterManager as BaseFilterManager } from './filter_manager/filter_manager';
+import { SearchSource } from '../legacy_imports';
-function createControlParams(id, label) {
+function createControlParams(id: string, label: string): ControlParams {
return {
- id: id,
+ id,
options: {},
- label: label,
- };
+ label,
+ } as ControlParams;
}
-let valueFromFilterBar;
-const mockFilterManager = {
+let valueFromFilterBar: any;
+const mockFilterManager: BaseFilterManager = {
getValueFromFilterBar: () => {
return valueFromFilterBar;
},
- createFilter: value => {
- return `mockKbnFilter:${value}`;
+ createFilter: (value: any) => {
+ return `mockKbnFilter:${value}` as any;
},
getIndexPattern: () => {
return 'mockIndexPattern';
},
-};
-const mockKbnApi = {};
+} as any;
+
+class ControlMock extends Control {
+ fetch() {
+ return Promise.resolve();
+ }
+
+ destroy() {}
+}
+const mockKbnApi: SearchSource = {} as SearchSource;
describe('hasChanged', () => {
- let control;
+ let control: ControlMock;
beforeEach(() => {
- control = new Control(createControlParams(3, 'control'), mockFilterManager, mockKbnApi);
+ control = new ControlMock(
+ createControlParams('3', 'control'),
+ mockFilterManager,
+ false,
+ mockKbnApi
+ );
});
afterEach(() => {
@@ -70,23 +86,26 @@ describe('hasChanged', () => {
});
describe('ancestors', () => {
- let grandParentControl;
- let parentControl;
- let childControl;
+ let grandParentControl: any;
+ let parentControl: any;
+ let childControl: any;
beforeEach(() => {
- grandParentControl = new Control(
- createControlParams(1, 'grandparent control'),
+ grandParentControl = new ControlMock(
+ createControlParams('1', 'grandparent control'),
mockFilterManager,
+ false,
mockKbnApi
);
- parentControl = new Control(
- createControlParams(2, 'parent control'),
+ parentControl = new ControlMock(
+ createControlParams('2', 'parent control'),
mockFilterManager,
+ false,
mockKbnApi
);
- childControl = new Control(
- createControlParams(3, 'child control'),
+ childControl = new ControlMock(
+ createControlParams('3', 'child control'),
mockFilterManager,
+ false,
mockKbnApi
);
});
@@ -122,7 +141,7 @@ describe('ancestors', () => {
});
describe('getAncestorValues', () => {
- let lastAncestorValues;
+ let lastAncestorValues: any[];
beforeEach(() => {
grandParentControl.set('myGrandParentValue');
parentControl.set('myParentValue');
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control.js b/src/legacy/core_plugins/input_control_vis/public/control/control.ts
similarity index 66%
rename from src/legacy/core_plugins/input_control_vis/public/control/control.js
rename to src/legacy/core_plugins/input_control_vis/public/control/control.ts
index 4035dc1adefe8..9dc03ecc23452 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/control.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/control.ts
@@ -22,32 +22,53 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
-export function noValuesDisableMsg(fieldName, indexPatternName) {
+import { esFilters } from '../../../../../plugins/data/public';
+import { SearchSource as SearchSourceClass } from '../legacy_imports';
+import { ControlParams, ControlParamsOptions, CONTROL_TYPES } from '../editor_utils';
+import { RangeFilterManager } from './filter_manager/range_filter_manager';
+import { PhraseFilterManager } from './filter_manager/phrase_filter_manager';
+import { FilterManager as BaseFilterManager } from './filter_manager/filter_manager';
+
+export function noValuesDisableMsg(fieldName: string, indexPatternName: string) {
return i18n.translate('inputControl.control.noValuesDisableTooltip', {
defaultMessage:
'Filtering occurs on the "{fieldName}" field, which doesn\'t exist on any documents in the "{indexPatternName}" \
index pattern. Choose a different field or index documents that contain values for this field.',
- values: { fieldName: fieldName, indexPatternName: indexPatternName },
+ values: { fieldName, indexPatternName },
});
}
-export function noIndexPatternMsg(indexPatternId) {
+export function noIndexPatternMsg(indexPatternId: string) {
return i18n.translate('inputControl.control.noIndexPatternTooltip', {
defaultMessage: 'Could not locate index-pattern id: {indexPatternId}.',
values: { indexPatternId },
});
}
-export class Control {
- constructor(controlParams, filterManager, useTimeFilter, SearchSource) {
+export abstract class Control {
+ private kbnFilter: esFilters.Filter | null = null;
+
+ enable: boolean = false;
+ disabledReason: string = '';
+ value: any;
+
+ id: string;
+ options: ControlParamsOptions;
+ type: CONTROL_TYPES;
+ label: string;
+ ancestors: Array> = [];
+
+ constructor(
+ public controlParams: ControlParams,
+ public filterManager: FilterManager,
+ public useTimeFilter: boolean,
+ public SearchSource: SearchSourceClass
+ ) {
this.id = controlParams.id;
this.controlParams = controlParams;
this.options = controlParams.options;
this.type = controlParams.type;
this.label = controlParams.label ? controlParams.label : controlParams.fieldName;
- this.useTimeFilter = useTimeFilter;
- this.filterManager = filterManager;
- this.SearchSource = SearchSource;
// restore state from kibana filter context
this.reset();
@@ -59,28 +80,20 @@ export class Control {
);
}
- async fetch() {
- throw new Error('fetch method not defined, subclass are required to implement');
- }
+ abstract fetch(query: string): Promise;
- destroy() {
- throw new Error('destroy method not defined, subclass are required to implement');
- }
+ abstract destroy(): void;
- format = value => {
+ format = (value: any) => {
const field = this.filterManager.getField();
- if (field) {
+ if (field?.format?.convert) {
return field.format.convert(value);
}
return value;
};
- /**
- *
- * @param ancestors {array of Controls}
- */
- setAncestors(ancestors) {
+ setAncestors(ancestors: Array>) {
this.ancestors = ancestors;
}
@@ -110,17 +123,17 @@ export class Control {
return this.enable;
}
- disable(reason) {
+ disable(reason: string) {
this.enable = false;
this.disabledReason = reason;
}
- set(newValue) {
+ set(newValue: any) {
this.value = newValue;
if (this.hasValue()) {
- this._kbnFilter = this.filterManager.createFilter(this.value);
+ this.kbnFilter = this.filterManager.createFilter(this.value);
} else {
- this._kbnFilter = null;
+ this.kbnFilter = null;
}
}
@@ -128,7 +141,7 @@ export class Control {
* Remove any user changes to value by resetting value to that as provided by Kibana filter pills
*/
reset() {
- this._kbnFilter = null;
+ this.kbnFilter = null;
this.value = this.filterManager.getValueFromFilterBar();
}
@@ -144,17 +157,17 @@ export class Control {
}
hasKbnFilter() {
- if (this._kbnFilter) {
+ if (this.kbnFilter) {
return true;
}
return false;
}
getKbnFilter() {
- return this._kbnFilter;
+ return this.kbnFilter;
}
- hasValue() {
+ hasValue(): boolean {
return this.value !== undefined;
}
}
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/control_factory.js b/src/legacy/core_plugins/input_control_vis/public/control/control_factory.ts
similarity index 86%
rename from src/legacy/core_plugins/input_control_vis/public/control/control_factory.js
rename to src/legacy/core_plugins/input_control_vis/public/control/control_factory.ts
index 6d3c7756f72aa..3dcc1d53d4211 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/control_factory.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/control_factory.ts
@@ -19,14 +19,15 @@
import { rangeControlFactory } from './range_control_factory';
import { listControlFactory } from './list_control_factory';
+import { ControlParams, CONTROL_TYPES } from '../editor_utils';
-export function controlFactory(controlParams) {
+export function getControlFactory(controlParams: ControlParams) {
let factory = null;
switch (controlParams.type) {
- case 'range':
+ case CONTROL_TYPES.RANGE:
factory = rangeControlFactory;
break;
- case 'list':
+ case CONTROL_TYPES.LIST:
factory = listControlFactory;
break;
default:
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.js b/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts
similarity index 71%
rename from src/legacy/core_plugins/input_control_vis/public/control/create_search_source.js
rename to src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts
index 2917dda5e96a7..c8fa5af5e052b 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/create_search_source.ts
@@ -16,15 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { timefilter } from 'ui/timefilter';
+
+import { esFilters, IndexPattern, TimefilterSetup } from '../../../../../plugins/data/public';
+import { SearchSource as SearchSourceClass, SearchSourceFields } from '../legacy_imports';
export function createSearchSource(
- SearchSource,
- initialState,
- indexPattern,
- aggs,
- useTimeFilter,
- filters = []
+ SearchSource: SearchSourceClass,
+ initialState: SearchSourceFields | null,
+ indexPattern: IndexPattern,
+ aggs: any,
+ useTimeFilter: boolean,
+ filters: esFilters.PhraseFilter[] = [],
+ timefilter: TimefilterSetup['timefilter']
) {
const searchSource = initialState ? new SearchSource(initialState) : new SearchSource();
// Do not not inherit from rootSearchSource to avoid picking up time and globals
@@ -32,7 +35,10 @@ export function createSearchSource(
searchSource.setField('filter', () => {
const activeFilters = [...filters];
if (useTimeFilter) {
- activeFilters.push(timefilter.createFilter(indexPattern));
+ const filter = timefilter.createFilter(indexPattern);
+ if (filter) {
+ activeFilters.push(filter);
+ }
}
return activeFilters;
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
similarity index 66%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.js
rename to src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
index 95277ac073d75..fd2cbae121b7e 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts
@@ -18,30 +18,45 @@
*/
import expect from '@kbn/expect';
+
import { FilterManager } from './filter_manager';
+import { coreMock } from '../../../../../../core/public/mocks';
+import {
+ esFilters,
+ IndexPattern,
+ FilterManager as QueryFilterManager,
+} from '../../../../../../plugins/data/public';
+
+const setupMock = coreMock.createSetup();
+
+class FilterManagerTest extends FilterManager {
+ createFilter() {
+ return {} as esFilters.Filter;
+ }
+
+ getValueFromFilterBar() {
+ return null;
+ }
+}
describe('FilterManager', function() {
const controlId = 'control1';
describe('findFilters', function() {
- const indexPatternMock = {};
- let kbnFilters;
- const queryFilterMock = {
- getAppFilters: () => {
- return kbnFilters;
- },
- getGlobalFilters: () => {
- return [];
- },
- };
- let filterManager;
+ const indexPatternMock = {} as IndexPattern;
+ let kbnFilters: esFilters.Filter[];
+ const queryFilterMock = new QueryFilterManager(setupMock.uiSettings);
+ queryFilterMock.getAppFilters = () => kbnFilters;
+ queryFilterMock.getGlobalFilters = () => [];
+
+ let filterManager: FilterManagerTest;
beforeEach(() => {
kbnFilters = [];
- filterManager = new FilterManager(controlId, 'field1', indexPatternMock, queryFilterMock);
+ filterManager = new FilterManagerTest(controlId, 'field1', indexPatternMock, queryFilterMock);
});
test('should not find filters that are not controlled by any visualization', function() {
- kbnFilters.push({});
+ kbnFilters.push({} as esFilters.Filter);
const foundFilters = filterManager.findFilters();
expect(foundFilters.length).to.be(0);
});
@@ -51,7 +66,7 @@ describe('FilterManager', function() {
meta: {
controlledBy: 'anotherControl',
},
- });
+ } as esFilters.Filter);
const foundFilters = filterManager.findFilters();
expect(foundFilters.length).to.be(0);
});
@@ -61,7 +76,7 @@ describe('FilterManager', function() {
meta: {
controlledBy: controlId,
},
- });
+ } as esFilters.Filter);
const foundFilters = filterManager.findFilters();
expect(foundFilters.length).to.be(1);
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
similarity index 62%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.js
rename to src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
index 672f56746cf80..d80a74ed46eae 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/filter_manager.ts
@@ -19,15 +19,33 @@
import _ from 'lodash';
-export class FilterManager {
- constructor(controlId, fieldName, indexPattern, queryFilter) {
- this.controlId = controlId;
- this.fieldName = fieldName;
- this.indexPattern = indexPattern;
- this.queryFilter = queryFilter;
- }
+import {
+ FilterManager as QueryFilterManager,
+ IndexPattern,
+ esFilters,
+} from '../../../../../../plugins/data/public';
+
+export abstract class FilterManager {
+ constructor(
+ public controlId: string,
+ public fieldName: string,
+ public indexPattern: IndexPattern,
+ public queryFilter: QueryFilterManager
+ ) {}
+
+ /**
+ * Convert phrases into filter
+ *
+ * @param {any[]} phrases
+ * @returns PhraseFilter
+ * single phrase: match query
+ * multiple phrases: bool query with should containing list of match_phrase queries
+ */
+ abstract createFilter(phrases: any): esFilters.Filter;
+
+ abstract getValueFromFilterBar(): any;
- getIndexPattern() {
+ getIndexPattern(): IndexPattern {
return this.indexPattern;
}
@@ -35,11 +53,7 @@ export class FilterManager {
return this.indexPattern.fields.getByName(this.fieldName);
}
- createFilter() {
- throw new Error('Must implement createFilter.');
- }
-
- findFilters() {
+ findFilters(): esFilters.Filter[] {
const kbnFilters = _.flatten([
this.queryFilter.getAppFilters(),
this.queryFilter.getGlobalFilters(),
@@ -48,8 +62,4 @@ export class FilterManager {
return _.get(kbnFilter, 'meta.controlledBy') === this.controlId;
});
}
-
- getValueFromFilterBar() {
- throw new Error('Must implement getValueFromFilterBar.');
- }
}
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
similarity index 79%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.js
rename to src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
index 7aa1ec6632043..dc577ca7168d1 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts
@@ -18,6 +18,12 @@
*/
import expect from '@kbn/expect';
+
+import {
+ esFilters,
+ IndexPattern,
+ FilterManager as QueryFilterManager,
+} from '../../../../../../plugins/data/public';
import { PhraseFilterManager } from './phrase_filter_manager';
describe('PhraseFilterManager', function() {
@@ -28,22 +34,20 @@ describe('PhraseFilterManager', function() {
const fieldMock = {
name: 'field1',
format: {
- convert: val => {
- return val;
- },
+ convert: (value: any) => value,
},
};
- const indexPatternMock = {
+ const indexPatternMock: IndexPattern = {
id: indexPatternId,
fields: {
- getByName: name => {
- const fields = { field1: fieldMock };
+ getByName: (name: string) => {
+ const fields: any = { field1: fieldMock };
return fields[name];
},
},
- };
- const queryFilterMock = {};
- let filterManager;
+ } as IndexPattern;
+ const queryFilterMock: QueryFilterManager = {} as QueryFilterManager;
+ let filterManager: PhraseFilterManager;
beforeEach(() => {
filterManager = new PhraseFilterManager(
controlId,
@@ -83,22 +87,32 @@ describe('PhraseFilterManager', function() {
});
describe('getValueFromFilterBar', function() {
- const indexPatternMock = {};
- const queryFilterMock = {};
- let filterManager;
- beforeEach(() => {
- class MockFindFiltersPhraseFilterManager extends PhraseFilterManager {
- constructor(controlId, fieldName, indexPattern, queryFilter, delimiter) {
- super(controlId, fieldName, indexPattern, queryFilter, delimiter);
- this.mockFilters = [];
- }
- findFilters() {
- return this.mockFilters;
- }
- setMockFilters(mockFilters) {
- this.mockFilters = mockFilters;
- }
+ class MockFindFiltersPhraseFilterManager extends PhraseFilterManager {
+ mockFilters: esFilters.Filter[];
+
+ constructor(
+ id: string,
+ fieldName: string,
+ indexPattern: IndexPattern,
+ queryFilter: QueryFilterManager
+ ) {
+ super(id, fieldName, indexPattern, queryFilter);
+ this.mockFilters = [];
}
+
+ findFilters() {
+ return this.mockFilters;
+ }
+
+ setMockFilters(mockFilters: esFilters.Filter[]) {
+ this.mockFilters = mockFilters;
+ }
+ }
+
+ const indexPatternMock: IndexPattern = {} as IndexPattern;
+ const queryFilterMock: QueryFilterManager = {} as QueryFilterManager;
+ let filterManager: MockFindFiltersPhraseFilterManager;
+ beforeEach(() => {
filterManager = new MockFindFiltersPhraseFilterManager(
controlId,
'field1',
@@ -119,7 +133,7 @@ describe('PhraseFilterManager', function() {
},
},
},
- ]);
+ ] as esFilters.Filter[]);
expect(filterManager.getValueFromFilterBar()).to.eql(['ios']);
});
@@ -145,7 +159,7 @@ describe('PhraseFilterManager', function() {
},
},
},
- ]);
+ ] as esFilters.Filter[]);
expect(filterManager.getValueFromFilterBar()).to.eql(['ios', 'win xp']);
});
@@ -169,7 +183,7 @@ describe('PhraseFilterManager', function() {
},
},
},
- ]);
+ ] as esFilters.Filter[]);
expect(filterManager.getValueFromFilterBar()).to.eql(['ios', 'win xp']);
});
@@ -185,7 +199,7 @@ describe('PhraseFilterManager', function() {
},
},
},
- ]);
+ ] as esFilters.Filter[]);
expect(filterManager.getValueFromFilterBar()).to.eql(undefined);
});
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
similarity index 68%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.js
rename to src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
index 1e60f8c4ebb67..b0b46be86f1e8 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts
@@ -18,37 +18,38 @@
*/
import _ from 'lodash';
-import { FilterManager } from './filter_manager.js';
-import { esFilters } from '../../../../../../plugins/data/public';
+
+import { FilterManager } from './filter_manager';
+import {
+ esFilters,
+ IndexPattern,
+ FilterManager as QueryFilterManager,
+} from '../../../../../../plugins/data/public';
export class PhraseFilterManager extends FilterManager {
- constructor(controlId, fieldName, indexPattern, queryFilter) {
+ constructor(
+ controlId: string,
+ fieldName: string,
+ indexPattern: IndexPattern,
+ queryFilter: QueryFilterManager
+ ) {
super(controlId, fieldName, indexPattern, queryFilter);
}
- /**
- * Convert phrases into filter
- *
- * @param {array} phrases
- * @return {object} query filter
- * single phrase: match query
- * multiple phrases: bool query with should containing list of match_phrase queries
- */
- createFilter(phrases) {
- let newFilter;
+ createFilter(phrases: any): esFilters.PhraseFilter {
+ let newFilter: esFilters.PhraseFilter;
+ const value = this.indexPattern.fields.getByName(this.fieldName);
+
+ if (!value) {
+ throw new Error(`Unable to find field with name: ${this.fieldName} on indexPattern`);
+ }
+
if (phrases.length === 1) {
- newFilter = esFilters.buildPhraseFilter(
- this.indexPattern.fields.getByName(this.fieldName),
- phrases[0],
- this.indexPattern
- );
+ newFilter = esFilters.buildPhraseFilter(value, phrases[0], this.indexPattern);
} else {
- newFilter = esFilters.buildPhrasesFilter(
- this.indexPattern.fields.getByName(this.fieldName),
- phrases,
- this.indexPattern
- );
+ newFilter = esFilters.buildPhrasesFilter(value, phrases, this.indexPattern);
}
+
newFilter.meta.key = this.fieldName;
newFilter.meta.controlledBy = this.controlId;
return newFilter;
@@ -62,7 +63,7 @@ export class PhraseFilterManager extends FilterManager {
const values = kbnFilters
.map(kbnFilter => {
- return this._getValueFromFilter(kbnFilter);
+ return this.getValueFromFilter(kbnFilter);
})
.filter(value => value != null);
@@ -78,15 +79,15 @@ export class PhraseFilterManager extends FilterManager {
/**
* Extract filtering value from kibana filters
*
- * @param {object} kbnFilter
+ * @param {esFilters.PhraseFilter} kbnFilter
* @return {Array.} array of values pulled from filter
*/
- _getValueFromFilter(kbnFilter) {
+ private getValueFromFilter(kbnFilter: esFilters.PhraseFilter): any {
// bool filter - multiple phrase filters
if (_.has(kbnFilter, 'query.bool.should')) {
- return _.get(kbnFilter, 'query.bool.should')
- .map(kbnFilter => {
- return this._getValueFromFilter(kbnFilter);
+ return _.get(kbnFilter, 'query.bool.should')
+ .map(kbnQueryFilter => {
+ return this.getValueFromFilter(kbnQueryFilter);
})
.filter(value => {
if (value) {
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
similarity index 68%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.js
rename to src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
index ffe2ebdad53bc..f4993a60c5b39 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts
@@ -18,7 +18,13 @@
*/
import expect from '@kbn/expect';
+
import { RangeFilterManager } from './range_filter_manager';
+import {
+ esFilters,
+ IndexPattern,
+ FilterManager as QueryFilterManager,
+} from '../../../../../../plugins/data/public';
describe('RangeFilterManager', function() {
const controlId = 'control1';
@@ -28,19 +34,19 @@ describe('RangeFilterManager', function() {
const fieldMock = {
name: 'field1',
};
- const indexPatternMock = {
+ const indexPatternMock: IndexPattern = {
id: indexPatternId,
fields: {
- getByName: name => {
- const fields = {
+ getByName: (name: any) => {
+ const fields: any = {
field1: fieldMock,
};
return fields[name];
},
},
- };
- const queryFilterMock = {};
- let filterManager;
+ } as IndexPattern;
+ const queryFilterMock: QueryFilterManager = {} as QueryFilterManager;
+ let filterManager: RangeFilterManager;
beforeEach(() => {
filterManager = new RangeFilterManager(
controlId,
@@ -62,22 +68,32 @@ describe('RangeFilterManager', function() {
});
describe('getValueFromFilterBar', function() {
- const indexPatternMock = {};
- const queryFilterMock = {};
- let filterManager;
- beforeEach(() => {
- class MockFindFiltersRangeFilterManager extends RangeFilterManager {
- constructor(controlId, fieldName, indexPattern, queryFilter) {
- super(controlId, fieldName, indexPattern, queryFilter);
- this.mockFilters = [];
- }
- findFilters() {
- return this.mockFilters;
- }
- setMockFilters(mockFilters) {
- this.mockFilters = mockFilters;
- }
+ class MockFindFiltersRangeFilterManager extends RangeFilterManager {
+ mockFilters: esFilters.RangeFilter[];
+
+ constructor(
+ id: string,
+ fieldName: string,
+ indexPattern: IndexPattern,
+ queryFilter: QueryFilterManager
+ ) {
+ super(id, fieldName, indexPattern, queryFilter);
+ this.mockFilters = [];
+ }
+
+ findFilters() {
+ return this.mockFilters;
+ }
+
+ setMockFilters(mockFilters: esFilters.RangeFilter[]) {
+ this.mockFilters = mockFilters;
}
+ }
+
+ const indexPatternMock: IndexPattern = {} as IndexPattern;
+ const queryFilterMock: QueryFilterManager = {} as QueryFilterManager;
+ let filterManager: MockFindFiltersRangeFilterManager;
+ beforeEach(() => {
filterManager = new MockFindFiltersRangeFilterManager(
controlId,
'field1',
@@ -95,14 +111,15 @@ describe('RangeFilterManager', function() {
lt: 3,
},
},
+ meta: {} as esFilters.RangeFilterMeta,
},
- ]);
+ ] as esFilters.RangeFilter[]);
const value = filterManager.getValueFromFilterBar();
expect(value).to.be.a('object');
expect(value).to.have.property('min');
- expect(value.min).to.be(1);
+ expect(value?.min).to.be(1);
expect(value).to.have.property('max');
- expect(value.max).to.be(3);
+ expect(value?.max).to.be(3);
});
test('should return undefined when filter value can not be extracted from Kibana filter', function() {
@@ -114,8 +131,9 @@ describe('RangeFilterManager', function() {
lte: 3,
},
},
+ meta: {} as esFilters.RangeFilterMeta,
},
- ]);
+ ] as esFilters.RangeFilter[]);
expect(filterManager.getValueFromFilterBar()).to.eql(undefined);
});
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.js b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
similarity index 77%
rename from src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.js
rename to src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
index 5a2e7b7d779bc..0a6819bd68e6f 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts
@@ -18,11 +18,17 @@
*/
import _ from 'lodash';
-import { FilterManager } from './filter_manager.js';
-import { esFilters } from '../../../../../../plugins/data/public';
+
+import { FilterManager } from './filter_manager';
+import { esFilters, IFieldType } from '../../../../../../plugins/data/public';
+
+interface SliderValue {
+ min?: string | number;
+ max?: string | number;
+}
// Convert slider value into ES range filter
-function toRange(sliderValue) {
+function toRange(sliderValue: SliderValue) {
return {
gte: sliderValue.min,
lte: sliderValue.max,
@@ -30,8 +36,8 @@ function toRange(sliderValue) {
}
// Convert ES range filter into slider value
-function fromRange(range) {
- const sliderValue = {};
+function fromRange(range: esFilters.RangeFilterParams): SliderValue {
+ const sliderValue: SliderValue = {};
if (_.has(range, 'gte')) {
sliderValue.min = _.get(range, 'gte');
}
@@ -54,9 +60,10 @@ export class RangeFilterManager extends FilterManager {
* @param {object} react-input-range value - POJO with `min` and `max` properties
* @return {object} range filter
*/
- createFilter(value) {
+ createFilter(value: SliderValue): esFilters.RangeFilter {
const newFilter = esFilters.buildRangeFilter(
- this.indexPattern.fields.getByName(this.fieldName),
+ // TODO: Fix type to be required
+ this.indexPattern.fields.getByName(this.fieldName) as IFieldType,
toRange(value),
this.indexPattern
);
@@ -65,13 +72,13 @@ export class RangeFilterManager extends FilterManager {
return newFilter;
}
- getValueFromFilterBar() {
+ getValueFromFilterBar(): SliderValue | undefined {
const kbnFilters = this.findFilters();
if (kbnFilters.length === 0) {
return;
}
- let range;
+ let range: esFilters.RangeFilterParams;
if (_.has(kbnFilters[0], 'script')) {
range = _.get(kbnFilters[0], 'script.script.params');
} else {
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.js b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.ts
similarity index 57%
rename from src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.js
rename to src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.ts
index 3b5ef7372bc1f..2420907727638 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.test.ts
@@ -17,92 +17,33 @@
* under the License.
*/
-import chrome from 'ui/chrome';
-import { listControlFactory } from './list_control_factory';
+import { listControlFactory, ListControl } from './list_control_factory';
+import { ControlParams, CONTROL_TYPES } from '../editor_utils';
+import { getDepsMock } from '../components/editor/__tests__/get_deps_mock';
+import { getSearchSourceMock } from '../components/editor/__tests__/get_search_service_mock';
-jest.mock('ui/timefilter', () => ({
- createFilter: jest.fn(),
-}));
+const MockSearchSource = getSearchSourceMock();
+const deps = getDepsMock();
-jest.mock('ui/new_platform', () => ({
- npStart: {
- plugins: {
- data: {
- query: {
- filterManager: {
- fieldName: 'myNumberField',
- getIndexPattern: () => ({
- fields: {
- getByName: name => {
- const fields = { myField: { name: 'myField' } };
- return fields[name];
- },
- },
- }),
- getAppFilters: jest.fn().mockImplementation(() => []),
- getGlobalFilters: jest.fn().mockImplementation(() => []),
- },
- },
- indexPatterns: {
- get: () => ({
- fields: {
- getByName: name => {
- const fields = { myField: { name: 'myField' } };
- return fields[name];
- },
- },
- }),
- },
- },
- },
- },
+jest.doMock('./create_search_source.ts', () => ({
+ createSearchSource: MockSearchSource,
}));
-chrome.getInjected.mockImplementation(key => {
- switch (key) {
- case 'autocompleteTimeout':
- return 1000;
- case 'autocompleteTerminateAfter':
- return 100000;
- }
-});
-
-function MockSearchSource() {
- return {
- setParent: () => {},
- setField: () => {},
- fetch: async () => {
- return {
- aggregations: {
- termsAgg: {
- buckets: [
- {
- key: 'Zurich Airport',
- doc_count: 691,
- },
- {
- key: 'Xi an Xianyang International Airport',
- doc_count: 526,
- },
- ],
- },
- },
- };
- },
- };
-}
-
describe('hasValue', () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
fieldName: 'myField',
- options: {},
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
};
const useTimeFilter = false;
- let listControl;
+ let listControl: ListControl;
beforeEach(async () => {
- listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource);
+ listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource, deps);
});
test('should be false when control has no value', () => {
@@ -121,22 +62,25 @@ describe('hasValue', () => {
});
describe('fetch', () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
fieldName: 'myField',
- options: {},
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
};
const useTimeFilter = false;
- const SearchSource = jest.fn(MockSearchSource);
- let listControl;
+ let listControl: ListControl;
beforeEach(async () => {
- listControl = await listControlFactory(controlParams, useTimeFilter, SearchSource);
+ listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource, deps);
});
test('should pass in timeout parameters from injected vars', async () => {
await listControl.fetch();
- expect(SearchSource).toHaveBeenCalledWith({
+ expect(MockSearchSource).toHaveBeenCalledWith({
timeout: `1000ms`,
terminate_after: 100000,
});
@@ -152,24 +96,37 @@ describe('fetch', () => {
});
describe('fetch with ancestors', () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
fieldName: 'myField',
- options: {},
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
};
const useTimeFilter = false;
- let listControl;
+ let listControl: ListControl;
let parentControl;
beforeEach(async () => {
- listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource);
+ listControl = await listControlFactory(controlParams, useTimeFilter, MockSearchSource, deps);
- const parentControlParams = {
+ const parentControlParams: ControlParams = {
id: 'parent',
fieldName: 'myField',
- options: {},
+ options: {} as any,
+ type: CONTROL_TYPES.LIST,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: 'parent',
};
- parentControl = await listControlFactory(parentControlParams, useTimeFilter, MockSearchSource);
+ parentControl = await listControlFactory(
+ parentControlParams,
+ useTimeFilter,
+ MockSearchSource,
+ deps
+ );
parentControl.clear();
listControl.setAncestors([parentControl]);
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.js b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts
similarity index 63%
rename from src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.js
rename to src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts
index d90b21eead5c6..56b42f295ce15 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/list_control_factory.ts
@@ -18,20 +18,30 @@
*/
import _ from 'lodash';
+import { i18n } from '@kbn/i18n';
+
+import { SearchSource as SearchSourceClass, SearchSourceFields } from '../legacy_imports';
import { Control, noValuesDisableMsg, noIndexPatternMsg } from './control';
import { PhraseFilterManager } from './filter_manager/phrase_filter_manager';
import { createSearchSource } from './create_search_source';
-import { i18n } from '@kbn/i18n';
-import { npStart } from 'ui/new_platform';
-import chrome from 'ui/chrome';
+import { ControlParams } from '../editor_utils';
+import { InputControlVisDependencies } from '../plugin';
+import { IFieldType, TimefilterSetup } from '../../../../../plugins/data/public';
function getEscapedQuery(query = '') {
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#_standard_operators
return query.replace(/[.?+*|{}[\]()"\\#@&<>~]/g, match => `\\${match}`);
}
-const termsAgg = ({ field, size, direction, query }) => {
- const terms = {
+interface TermsAggArgs {
+ field?: IFieldType;
+ size: number | null;
+ direction: string;
+ query?: string;
+}
+
+const termsAgg = ({ field, size, direction, query }: TermsAggArgs) => {
+ const terms: any = {
order: {
_count: direction,
},
@@ -41,14 +51,14 @@ const termsAgg = ({ field, size, direction, query }) => {
terms.size = size < 1 ? 1 : size;
}
- if (field.scripted) {
+ if (field?.scripted) {
terms.script = {
source: field.script,
lang: field.lang,
};
terms.value_type = field.type === 'number' ? 'float' : field.type;
} else {
- terms.field = field.name;
+ terms.field = field?.name;
}
if (query) {
@@ -57,13 +67,34 @@ const termsAgg = ({ field, size, direction, query }) => {
return {
termsAgg: {
- terms: terms,
+ terms,
},
};
};
-class ListControl extends Control {
- fetch = async query => {
+export class ListControl extends Control {
+ private getInjectedVar: InputControlVisDependencies['core']['injectedMetadata']['getInjectedVar'];
+ private timefilter: TimefilterSetup['timefilter'];
+
+ abortController?: AbortController;
+ lastAncestorValues: any;
+ lastQuery?: string;
+ partialResults?: boolean;
+ selectOptions?: string[];
+
+ constructor(
+ controlParams: ControlParams,
+ filterManager: PhraseFilterManager,
+ useTimeFilter: boolean,
+ SearchSource: SearchSourceClass,
+ deps: InputControlVisDependencies
+ ) {
+ super(controlParams, filterManager, useTimeFilter, SearchSource);
+ this.getInjectedVar = deps.core.injectedMetadata.getInjectedVar;
+ this.timefilter = deps.data.query.timefilter.timefilter;
+ }
+
+ fetch = async (query?: string) => {
// Abort any in-progress fetch
if (this.abortController) {
this.abortController.abort();
@@ -101,9 +132,9 @@ class ListControl extends Control {
}
const fieldName = this.filterManager.fieldName;
- const initialSearchSourceState = {
- timeout: `${chrome.getInjected('autocompleteTimeout')}ms`,
- terminate_after: chrome.getInjected('autocompleteTerminateAfter'),
+ const initialSearchSourceState: SearchSourceFields = {
+ timeout: `${this.getInjectedVar('autocompleteTimeout')}ms`,
+ terminate_after: Number(this.getInjectedVar('autocompleteTerminateAfter')),
};
const aggs = termsAgg({
field: indexPattern.fields.getByName(fieldName),
@@ -117,7 +148,8 @@ class ListControl extends Control {
indexPattern,
aggs,
this.useTimeFilter,
- ancestorFilters
+ ancestorFilters,
+ this.timefilter
);
const abortSignal = this.abortController.signal;
@@ -143,8 +175,8 @@ class ListControl extends Control {
return;
}
- const selectOptions = _.get(resp, 'aggregations.termsAgg.buckets', []).map(bucket => {
- return bucket.key;
+ const selectOptions = _.get(resp, 'aggregations.termsAgg.buckets', []).map((bucket: any) => {
+ return bucket?.key;
});
if (selectOptions.length === 0 && !query) {
@@ -167,29 +199,34 @@ class ListControl extends Control {
}
}
-export async function listControlFactory(controlParams, useTimeFilter, SearchSource) {
- let indexPattern;
- try {
- indexPattern = await npStart.plugins.data.indexPatterns.get(controlParams.indexPattern);
-
- // dynamic options are only allowed on String fields but the setting defaults to true so it could
- // be enabled for non-string fields (since UI input is hidden for non-string fields).
- // If field is not string, then disable dynamic options.
- const field = indexPattern.fields.find(field => {
- return field.name === controlParams.fieldName;
- });
- if (field && field.type !== 'string') {
- controlParams.options.dynamicOptions = false;
- }
- } catch (err) {
- // ignore not found error and return control so it can be displayed in disabled state.
+export async function listControlFactory(
+ controlParams: ControlParams,
+ useTimeFilter: boolean,
+ SearchSource: SearchSourceClass,
+ deps: InputControlVisDependencies
+) {
+ const [, { data: dataPluginStart }] = await deps.core.getStartServices();
+ const indexPattern = await dataPluginStart.indexPatterns.get(controlParams.indexPattern);
+
+ // dynamic options are only allowed on String fields but the setting defaults to true so it could
+ // be enabled for non-string fields (since UI input is hidden for non-string fields).
+ // If field is not string, then disable dynamic options.
+ const field = indexPattern.fields.find(({ name }) => name === controlParams.fieldName);
+ if (field && field.type !== 'string') {
+ controlParams.options.dynamicOptions = false;
}
- const { filterManager } = npStart.plugins.data.query;
- return new ListControl(
+ const listControl = new ListControl(
controlParams,
- new PhraseFilterManager(controlParams.id, controlParams.fieldName, indexPattern, filterManager),
+ new PhraseFilterManager(
+ controlParams.id,
+ controlParams.fieldName,
+ indexPattern,
+ deps.data.query.filterManager
+ ),
useTimeFilter,
- SearchSource
+ SearchSource,
+ deps
);
+ return listControl;
}
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.js b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.ts
similarity index 59%
rename from src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.js
rename to src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.ts
index b545c6e2834f3..5328aeb6c6a47 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.test.ts
@@ -18,74 +18,37 @@
*/
import { rangeControlFactory } from './range_control_factory';
+import { ControlParams, CONTROL_TYPES } from '../editor_utils';
+import { getSearchSourceMock } from '../components/editor/__tests__/get_search_service_mock';
+import { getDepsMock } from '../components/editor/__tests__/get_deps_mock';
-let esSearchResponse;
-class MockSearchSource {
- setParent() {}
- setField() {}
- async fetch() {
- return esSearchResponse;
- }
-}
-
-jest.mock('ui/timefilter', () => ({
- createFilter: jest.fn(),
-}));
-
-jest.mock('ui/new_platform', () => ({
- npStart: {
- plugins: {
- data: {
- query: {
- filterManager: {
- fieldName: 'myNumberField',
- getIndexPattern: () => ({
- fields: {
- getByName: name => {
- const fields = { myNumberField: { name: 'myNumberField' } };
- return fields[name];
- },
- },
- }),
- getAppFilters: jest.fn().mockImplementation(() => []),
- getGlobalFilters: jest.fn().mockImplementation(() => []),
- },
- },
- indexPatterns: {
- get: () => ({
- fields: {
- getByName: name => {
- const fields = { myNumberField: { name: 'myNumberField' } };
- return fields[name];
- },
- },
- }),
- },
- },
- },
- },
-}));
+const deps = getDepsMock();
describe('fetch', () => {
- const controlParams = {
+ const controlParams: ControlParams = {
id: '1',
fieldName: 'myNumberField',
options: {},
+ type: CONTROL_TYPES.RANGE,
+ label: 'test',
+ indexPattern: {} as any,
+ parent: {} as any,
};
const useTimeFilter = false;
- let rangeControl;
- beforeEach(async () => {
- rangeControl = await rangeControlFactory(controlParams, useTimeFilter, MockSearchSource);
- });
-
test('should set min and max from aggregation results', async () => {
- esSearchResponse = {
+ const esSearchResponse = {
aggregations: {
maxAgg: { value: 100 },
minAgg: { value: 10 },
},
};
+ const rangeControl = await rangeControlFactory(
+ controlParams,
+ useTimeFilter,
+ getSearchSourceMock(esSearchResponse),
+ deps
+ );
await rangeControl.fetch();
expect(rangeControl.isEnabled()).toBe(true);
@@ -95,12 +58,18 @@ describe('fetch', () => {
test('should disable control when there are 0 hits', async () => {
// ES response when the query does not match any documents
- esSearchResponse = {
+ const esSearchResponse = {
aggregations: {
maxAgg: { value: null },
minAgg: { value: null },
},
};
+ const rangeControl = await rangeControlFactory(
+ controlParams,
+ useTimeFilter,
+ getSearchSourceMock(esSearchResponse),
+ deps
+ );
await rangeControl.fetch();
expect(rangeControl.isEnabled()).toBe(false);
@@ -109,7 +78,13 @@ describe('fetch', () => {
test('should disable control when response is empty', async () => {
// ES response for dashboardonly user who does not have read permissions on index is 200 (which is weird)
// and there is not aggregations key
- esSearchResponse = {};
+ const esSearchResponse = {};
+ const rangeControl = await rangeControlFactory(
+ controlParams,
+ useTimeFilter,
+ getSearchSourceMock(esSearchResponse),
+ deps
+ );
await rangeControl.fetch();
expect(rangeControl.isEnabled()).toBe(false);
diff --git a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.js b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts
similarity index 63%
rename from src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.js
rename to src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts
index c99c794c1fcd5..b9191436b5968 100644
--- a/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.js
+++ b/src/legacy/core_plugins/input_control_vis/public/control/range_control_factory.ts
@@ -18,22 +18,29 @@
*/
import _ from 'lodash';
+import { i18n } from '@kbn/i18n';
+
+import { SearchSource as SearchSourceClass } from '../legacy_imports';
import { Control, noValuesDisableMsg, noIndexPatternMsg } from './control';
import { RangeFilterManager } from './filter_manager/range_filter_manager';
import { createSearchSource } from './create_search_source';
-import { i18n } from '@kbn/i18n';
-import { npStart } from 'ui/new_platform';
+import { ControlParams } from '../editor_utils';
+import { InputControlVisDependencies } from '../plugin';
+import { IFieldType, TimefilterSetup } from '../.../../../../../../plugins/data/public';
-const minMaxAgg = field => {
- const aggBody = {};
- if (field.scripted) {
- aggBody.script = {
- source: field.script,
- lang: field.lang,
- };
- } else {
- aggBody.field = field.name;
+const minMaxAgg = (field?: IFieldType) => {
+ const aggBody: any = {};
+ if (field) {
+ if (field.scripted) {
+ aggBody.script = {
+ source: field.script,
+ lang: field.lang,
+ };
+ } else {
+ aggBody.field = field.name;
+ }
}
+
return {
maxAgg: {
max: aggBody,
@@ -44,7 +51,23 @@ const minMaxAgg = field => {
};
};
-class RangeControl extends Control {
+export class RangeControl extends Control {
+ timefilter: TimefilterSetup['timefilter'];
+ abortController: any;
+ min: any;
+ max: any;
+
+ constructor(
+ controlParams: ControlParams,
+ filterManager: RangeFilterManager,
+ useTimeFilter: boolean,
+ SearchSource: SearchSourceClass,
+ deps: InputControlVisDependencies
+ ) {
+ super(controlParams, filterManager, useTimeFilter, SearchSource);
+ this.timefilter = deps.data.query.timefilter.timefilter;
+ }
+
async fetch() {
// Abort any in-progress fetch
if (this.abortController) {
@@ -58,14 +81,15 @@ class RangeControl extends Control {
}
const fieldName = this.filterManager.fieldName;
-
const aggs = minMaxAgg(indexPattern.fields.getByName(fieldName));
const searchSource = createSearchSource(
this.SearchSource,
null,
indexPattern,
aggs,
- this.useTimeFilter
+ this.useTimeFilter,
+ [],
+ this.timefilter
);
const abortSignal = this.abortController.signal;
@@ -102,18 +126,25 @@ class RangeControl extends Control {
}
}
-export async function rangeControlFactory(controlParams, useTimeFilter, SearchSource) {
- let indexPattern;
- try {
- indexPattern = await npStart.plugins.data.indexPatterns.get(controlParams.indexPattern);
- } catch (err) {
- // ignore not found error and return control so it can be displayed in disabled state.
- }
- const { filterManager } = npStart.plugins.data.query;
+export async function rangeControlFactory(
+ controlParams: ControlParams,
+ useTimeFilter: boolean,
+ SearchSource: SearchSourceClass,
+ deps: InputControlVisDependencies
+): Promise {
+ const [, { data: dataPluginStart }] = await deps.core.getStartServices();
+ const indexPattern = await dataPluginStart.indexPatterns.get(controlParams.indexPattern);
+
return new RangeControl(
controlParams,
- new RangeFilterManager(controlParams.id, controlParams.fieldName, indexPattern, filterManager),
+ new RangeFilterManager(
+ controlParams.id,
+ controlParams.fieldName,
+ indexPattern,
+ deps.data.query.filterManager
+ ),
useTimeFilter,
- SearchSource
+ SearchSource,
+ deps
);
}
diff --git a/src/legacy/core_plugins/input_control_vis/public/editor_utils.js b/src/legacy/core_plugins/input_control_vis/public/editor_utils.ts
similarity index 64%
rename from src/legacy/core_plugins/input_control_vis/public/editor_utils.js
rename to src/legacy/core_plugins/input_control_vis/public/editor_utils.ts
index f5b4390342a0f..74def0a8d86f4 100644
--- a/src/legacy/core_plugins/input_control_vis/public/editor_utils.js
+++ b/src/legacy/core_plugins/input_control_vis/public/editor_utils.ts
@@ -16,21 +16,54 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { $Values } from '@kbn/utility-types';
export const CONTROL_TYPES = {
- LIST: 'list',
- RANGE: 'range',
+ LIST: 'list' as 'list',
+ RANGE: 'range' as 'range',
};
+export type CONTROL_TYPES = $Values;
-export const setControl = (controls, controlIndex, control) => [
+export interface ControlParamsOptions {
+ decimalPlaces?: number;
+ step?: number;
+ type?: string;
+ multiselect?: boolean;
+ dynamicOptions?: boolean;
+ size?: number;
+ order?: string;
+}
+
+export interface ControlParams {
+ id: string;
+ type: CONTROL_TYPES;
+ label: string;
+ fieldName: string;
+ indexPattern: string;
+ parent: string;
+ options: ControlParamsOptions;
+}
+
+export const setControl = (
+ controls: ControlParams[],
+ controlIndex: number,
+ control: ControlParams
+): ControlParams[] => [
...controls.slice(0, controlIndex),
control,
...controls.slice(controlIndex + 1),
];
-export const addControl = (controls, control) => [...controls, control];
+export const addControl = (controls: ControlParams[], control: ControlParams): ControlParams[] => [
+ ...controls,
+ control,
+];
-export const moveControl = (controls, controlIndex, direction) => {
+export const moveControl = (
+ controls: ControlParams[],
+ controlIndex: number,
+ direction: number
+): ControlParams[] => {
let newIndex;
if (direction >= 0) {
newIndex = controlIndex + 1;
@@ -54,13 +87,13 @@ export const moveControl = (controls, controlIndex, direction) => {
}
};
-export const removeControl = (controls, controlIndex) => [
+export const removeControl = (controls: ControlParams[], controlIndex: number): ControlParams[] => [
...controls.slice(0, controlIndex),
...controls.slice(controlIndex + 1),
];
-export const getDefaultOptions = type => {
- const defaultOptions = {};
+export const getDefaultOptions = (type: CONTROL_TYPES): ControlParamsOptions => {
+ const defaultOptions: ControlParamsOptions = {};
switch (type) {
case CONTROL_TYPES.RANGE:
defaultOptions.decimalPlaces = 0;
@@ -77,17 +110,17 @@ export const getDefaultOptions = type => {
return defaultOptions;
};
-export const newControl = type => ({
+export const newControl = (type: CONTROL_TYPES): ControlParams => ({
id: new Date().getTime().toString(),
indexPattern: '',
fieldName: '',
parent: '',
label: '',
- type: type,
+ type,
options: getDefaultOptions(type),
});
-export const getTitle = (controlParams, controlIndex) => {
+export const getTitle = (controlParams: ControlParams, controlIndex: number): string => {
let title = `${controlParams.type}: ${controlIndex}`;
if (controlParams.label) {
title = `${controlParams.label}`;
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_register.js b/src/legacy/core_plugins/input_control_vis/public/index.ts
similarity index 76%
rename from src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_register.js
rename to src/legacy/core_plugins/input_control_vis/public/index.ts
index c50cda56c7151..e14c2cc4b69b6 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_register.js
+++ b/src/legacy/core_plugins/input_control_vis/public/index.ts
@@ -17,9 +17,9 @@
* under the License.
*/
-import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry';
-import './saved_visualizations';
+import { PluginInitializerContext } from '../../../../core/public';
+import { InputControlVisPlugin as Plugin } from './plugin';
-SavedObjectRegistryProvider.register(savedVisualizations => {
- return savedVisualizations;
-});
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new Plugin(initializerContext);
+}
diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.js b/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts
similarity index 83%
rename from src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.js
rename to src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts
index 09c6749bcab94..aa1383587ea68 100644
--- a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/input_control_fn.test.ts
@@ -17,14 +17,15 @@
* under the License.
*/
-jest.mock('ui/new_platform');
+import { createInputControlVisFn } from './input_control_fn';
// eslint-disable-next-line
import { functionWrapper } from '../../../../plugins/expressions/public/functions/tests/utils';
-import { inputControlVis } from './input_control_fn';
+
+jest.mock('./legacy_imports.ts');
describe('interpreter/functions#input_control_vis', () => {
- const fn = functionWrapper(inputControlVis);
+ const fn = functionWrapper(createInputControlVisFn);
const visConfig = {
controls: [
{
@@ -47,8 +48,8 @@ describe('interpreter/functions#input_control_vis', () => {
pinFilters: false,
};
- it('returns an object with the correct structure', () => {
- const actual = fn(undefined, { visConfig: JSON.stringify(visConfig) });
+ it('returns an object with the correct structure', async () => {
+ const actual = await fn(null, { visConfig: JSON.stringify(visConfig) });
expect(actual).toMatchSnapshot();
});
});
diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.js b/src/legacy/core_plugins/input_control_vis/public/input_control_fn.ts
similarity index 70%
rename from src/legacy/core_plugins/input_control_vis/public/input_control_fn.js
rename to src/legacy/core_plugins/input_control_vis/public/input_control_fn.ts
index 0bd435f502a5d..0482c0d2cbff3 100644
--- a/src/legacy/core_plugins/input_control_vis/public/input_control_fn.js
+++ b/src/legacy/core_plugins/input_control_vis/public/input_control_fn.ts
@@ -17,10 +17,37 @@
* under the License.
*/
-import { functionsRegistry } from 'plugins/interpreter/registries';
import { i18n } from '@kbn/i18n';
-export const inputControlVis = () => ({
+import {
+ ExpressionFunction,
+ KibanaDatatable,
+ Render,
+} from '../../../../plugins/expressions/public';
+
+const name = 'input_control_vis';
+
+type Context = KibanaDatatable;
+
+interface Arguments {
+ visConfig: string;
+}
+
+type VisParams = Required;
+
+interface RenderValue {
+ visType: 'input_control_vis';
+ visConfig: VisParams;
+}
+
+type Return = Promise>;
+
+export const createInputControlVisFn = (): ExpressionFunction<
+ typeof name,
+ Context,
+ Arguments,
+ Return
+> => ({
name: 'input_control_vis',
type: 'render',
context: {
@@ -33,9 +60,10 @@ export const inputControlVis = () => ({
visConfig: {
types: ['string'],
default: '"{}"',
+ help: '',
},
},
- fn(context, args) {
+ async fn(context, args) {
const params = JSON.parse(args.visConfig);
return {
type: 'render',
@@ -47,5 +75,3 @@ export const inputControlVis = () => ({
};
},
});
-
-functionsRegistry.register(inputControlVis);
diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts b/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts
new file mode 100644
index 0000000000000..b6774aa87b43c
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts
@@ -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 { i18n } from '@kbn/i18n';
+
+import { createInputControlVisController } from './vis_controller';
+import { getControlsTab } from './components/editor/controls_tab';
+import { OptionsTab } from './components/editor/options_tab';
+import { Status, defaultFeedbackMessage } from '../../visualizations/public';
+import { InputControlVisDependencies } from './plugin';
+
+export function createInputControlVisTypeDefinition(deps: InputControlVisDependencies) {
+ const InputControlVisController = createInputControlVisController(deps);
+ const ControlsTab = getControlsTab(deps);
+
+ return {
+ name: 'input_control_vis',
+ title: i18n.translate('inputControl.register.controlsTitle', {
+ defaultMessage: 'Controls',
+ }),
+ icon: 'visControls',
+ description: i18n.translate('inputControl.register.controlsDescription', {
+ defaultMessage: 'Create interactive controls for easy dashboard manipulation.',
+ }),
+ stage: 'experimental',
+ requiresUpdateStatus: [Status.PARAMS, Status.TIME],
+ feedbackMessage: defaultFeedbackMessage,
+ visualization: InputControlVisController,
+ visConfig: {
+ defaults: {
+ controls: [],
+ updateFiltersOnChange: false,
+ useTimeFilter: false,
+ pinFilters: false,
+ },
+ },
+ editor: 'default',
+ editorConfig: {
+ optionTabs: [
+ {
+ name: 'controls',
+ title: i18n.translate('inputControl.register.tabs.controlsTitle', {
+ defaultMessage: 'Controls',
+ }),
+ editor: ControlsTab,
+ },
+ {
+ name: 'options',
+ title: i18n.translate('inputControl.register.tabs.optionsTitle', {
+ defaultMessage: 'Options',
+ }),
+ editor: OptionsTab,
+ },
+ ],
+ },
+ requestHandler: 'none',
+ responseHandler: 'none',
+ };
+}
diff --git a/src/legacy/core_plugins/input_control_vis/public/legacy.ts b/src/legacy/core_plugins/input_control_vis/public/legacy.ts
new file mode 100644
index 0000000000000..438cdffdb323a
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/public/legacy.ts
@@ -0,0 +1,49 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { PluginInitializerContext } from 'kibana/public';
+import { npSetup, npStart } from 'ui/new_platform';
+
+import { plugin } from '.';
+
+import {
+ InputControlVisPluginSetupDependencies,
+ InputControlVisPluginStartDependencies,
+} from './plugin';
+import {
+ setup as visualizationsSetup,
+ start as visualizationsStart,
+} from '../../visualizations/public/np_ready/public/legacy';
+
+const setupPlugins: Readonly = {
+ expressions: npSetup.plugins.expressions,
+ data: npSetup.plugins.data,
+ visualizations: visualizationsSetup,
+};
+
+const startPlugins: Readonly = {
+ expressions: npStart.plugins.expressions,
+ data: npStart.plugins.data,
+ visualizations: visualizationsStart,
+};
+
+const pluginInstance = plugin({} as PluginInitializerContext);
+
+export const setup = pluginInstance.setup(npSetup.core, setupPlugins);
+export const start = pluginInstance.start(npStart.core, startPlugins);
diff --git a/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts b/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts
new file mode 100644
index 0000000000000..864ce3b146689
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts
@@ -0,0 +1,29 @@
+/*
+ * 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 { SearchSource as SearchSourceClass } from 'ui/courier';
+import { Class } from '@kbn/utility-types';
+
+export { Vis, VisParams } from 'ui/vis';
+export { VisOptionsProps } from 'ui/vis/editors/default';
+export { ValidatedDualRange } from 'ui/validated_range';
+export { SearchSourceFields } from 'ui/courier/types';
+
+export type SearchSource = Class;
+export const SearchSource = SearchSourceClass;
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/index.js b/src/legacy/core_plugins/input_control_vis/public/lineage/index.ts
similarity index 100%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/index.js
rename to src/legacy/core_plugins/input_control_vis/public/lineage/index.ts
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.js b/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.ts
similarity index 94%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.js
rename to src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.ts
index de1b589b7dfa9..a0cd648007ecc 100644
--- a/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.test.ts
@@ -23,11 +23,11 @@ import { CONTROL_TYPES, newControl } from '../editor_utils';
test('creates lineage map', () => {
const control1 = newControl(CONTROL_TYPES.LIST);
- control1.id = 1;
+ control1.id = '1';
const control2 = newControl(CONTROL_TYPES.LIST);
- control2.id = 2;
+ control2.id = '2';
const control3 = newControl(CONTROL_TYPES.LIST);
- control3.id = 3;
+ control3.id = '3';
control2.parent = control1.id;
control3.parent = control2.id;
@@ -40,9 +40,9 @@ test('creates lineage map', () => {
test('safely handles circular graph', () => {
const control1 = newControl(CONTROL_TYPES.LIST);
- control1.id = 1;
+ control1.id = '1';
const control2 = newControl(CONTROL_TYPES.LIST);
- control2.id = 2;
+ control2.id = '2';
control1.parent = control2.id;
control2.parent = control1.id;
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.js b/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.ts
similarity index 80%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.js
rename to src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.ts
index a08c5d1670a09..d74782c373942 100644
--- a/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.js
+++ b/src/legacy/core_plugins/input_control_vis/public/lineage/lineage_map.ts
@@ -18,18 +18,19 @@
*/
import _ from 'lodash';
+import { ControlParams } from '../editor_utils';
-export function getLineageMap(controlParamsList) {
- function getControlParamsById(controlId) {
+export function getLineageMap(controlParamsList: ControlParams[]) {
+ function getControlParamsById(controlId: string) {
return controlParamsList.find(controlParams => {
return controlParams.id === controlId;
});
}
- const lineageMap = new Map();
+ const lineageMap = new Map();
controlParamsList.forEach(rootControlParams => {
const lineage = [rootControlParams.id];
- const getLineage = controlParams => {
+ const getLineage = (controlParams: ControlParams) => {
if (
_.has(controlParams, 'parent') &&
controlParams.parent !== '' &&
@@ -37,7 +38,10 @@ export function getLineageMap(controlParamsList) {
) {
lineage.push(controlParams.parent);
const parent = getControlParamsById(controlParams.parent);
- getLineage(parent);
+
+ if (parent) {
+ getLineage(parent);
+ }
}
};
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.js b/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.ts
similarity index 98%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.js
rename to src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.ts
index fe180357067a9..af6e2444b486f 100644
--- a/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.js
+++ b/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.test.ts
@@ -22,7 +22,7 @@ import { getLineageMap } from './lineage_map';
import { getParentCandidates } from './parent_candidates';
import { CONTROL_TYPES, newControl } from '../editor_utils';
-function createControlParams(id) {
+function createControlParams(id: any) {
const controlParams = newControl(CONTROL_TYPES.LIST);
controlParams.id = id;
controlParams.indexPattern = 'indexPatternId';
diff --git a/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.js b/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.ts
similarity index 85%
rename from src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.js
rename to src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.ts
index 17005c24dd41d..af4fddef19001 100644
--- a/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.js
+++ b/src/legacy/core_plugins/input_control_vis/public/lineage/parent_candidates.ts
@@ -17,9 +17,13 @@
* under the License.
*/
-import { getTitle } from '../editor_utils';
+import { getTitle, ControlParams } from '../editor_utils';
-export function getParentCandidates(controlParamsList, controlId, lineageMap) {
+export function getParentCandidates(
+ controlParamsList: ControlParams[],
+ controlId: string,
+ lineageMap: Map
+) {
return controlParamsList
.filter(controlParams => {
// Ignore controls that do not have index pattern and field set
@@ -28,7 +32,7 @@ export function getParentCandidates(controlParamsList, controlId, lineageMap) {
}
// Ignore controls that would create a circular graph
const lineage = lineageMap.get(controlParams.id);
- if (lineage.includes(controlId)) {
+ if (lineage?.includes(controlId)) {
return false;
}
return true;
diff --git a/src/legacy/core_plugins/input_control_vis/public/plugin.ts b/src/legacy/core_plugins/input_control_vis/public/plugin.ts
new file mode 100644
index 0000000000000..e9ffad8b35f21
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/public/plugin.ts
@@ -0,0 +1,70 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public';
+
+import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public';
+import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public';
+import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public';
+import { createInputControlVisFn } from './input_control_fn';
+import { createInputControlVisTypeDefinition } from './input_control_vis_type';
+
+type InputControlVisCoreSetup = CoreSetup;
+
+export interface InputControlVisDependencies {
+ core: InputControlVisCoreSetup;
+ data: DataPublicPluginSetup;
+}
+
+/** @internal */
+export interface InputControlVisPluginSetupDependencies {
+ expressions: ReturnType;
+ visualizations: VisualizationsSetup;
+ data: DataPublicPluginSetup;
+}
+
+/** @internal */
+export interface InputControlVisPluginStartDependencies {
+ expressions: ReturnType;
+ visualizations: VisualizationsStart;
+ data: DataPublicPluginStart;
+}
+
+/** @internal */
+export class InputControlVisPlugin implements Plugin, void> {
+ constructor(public initializerContext: PluginInitializerContext) {}
+
+ public async setup(
+ core: InputControlVisCoreSetup,
+ { expressions, visualizations, data }: InputControlVisPluginSetupDependencies
+ ) {
+ const visualizationDependencies: Readonly = {
+ core,
+ data,
+ };
+
+ expressions.registerFunction(createInputControlVisFn);
+ visualizations.types.createBaseVisualization(
+ createInputControlVisTypeDefinition(visualizationDependencies)
+ );
+ }
+
+ public start(core: CoreStart, deps: InputControlVisPluginStartDependencies) {
+ // nothing to do here
+ }
+}
diff --git a/src/legacy/core_plugins/input_control_vis/public/register_vis.js b/src/legacy/core_plugins/input_control_vis/public/register_vis.js
deleted file mode 100644
index 09993be3614f2..0000000000000
--- a/src/legacy/core_plugins/input_control_vis/public/register_vis.js
+++ /dev/null
@@ -1,72 +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 { VisController } from './vis_controller';
-import { ControlsTab } from './components/editor/controls_tab';
-import { OptionsTab } from './components/editor/options_tab';
-import { i18n } from '@kbn/i18n';
-import { setup as visualizations } from '../../visualizations/public/np_ready/public/legacy';
-import { Status, defaultFeedbackMessage } from '../../visualizations/public';
-
-export const inputControlVisDefinition = {
- name: 'input_control_vis',
- title: i18n.translate('inputControl.register.controlsTitle', {
- defaultMessage: 'Controls',
- }),
- icon: 'visControls',
- description: i18n.translate('inputControl.register.controlsDescription', {
- defaultMessage: 'Create interactive controls for easy dashboard manipulation.',
- }),
- stage: 'experimental',
- requiresUpdateStatus: [Status.PARAMS, Status.TIME],
- feedbackMessage: defaultFeedbackMessage,
- visualization: VisController,
- visConfig: {
- defaults: {
- controls: [],
- updateFiltersOnChange: false,
- useTimeFilter: false,
- pinFilters: false,
- },
- },
- editor: 'default',
- editorConfig: {
- optionTabs: [
- {
- name: 'controls',
- title: i18n.translate('inputControl.register.tabs.controlsTitle', {
- defaultMessage: 'Controls',
- }),
- editor: ControlsTab,
- },
- {
- name: 'options',
- title: i18n.translate('inputControl.register.tabs.optionsTitle', {
- defaultMessage: 'Options',
- }),
- editor: OptionsTab,
- },
- ],
- },
- requestHandler: 'none',
- responseHandler: 'none',
-};
-
-// register the provider with the visTypes registry
-visualizations.types.createBaseVisualization(inputControlVisDefinition);
diff --git a/src/legacy/core_plugins/input_control_vis/public/vis_controller.js b/src/legacy/core_plugins/input_control_vis/public/vis_controller.js
deleted file mode 100644
index 6a1e23769e28c..0000000000000
--- a/src/legacy/core_plugins/input_control_vis/public/vis_controller.js
+++ /dev/null
@@ -1,207 +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 React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import { I18nContext } from 'ui/i18n';
-import { InputControlVis } from './components/vis/input_control_vis';
-import { controlFactory } from './control/control_factory';
-import { getLineageMap } from './lineage';
-import { npStart } from 'ui/new_platform';
-import { SearchSource } from '../../../ui/public/courier/search_source/search_source';
-
-class VisController {
- constructor(el, vis) {
- this.el = el;
- this.vis = vis;
- this.controls = [];
-
- this.queryBarUpdateHandler = this.updateControlsFromKbn.bind(this);
-
- this.filterManager = npStart.plugins.data.query.filterManager;
- this.updateSubsciption = this.filterManager.getUpdates$().subscribe(this.queryBarUpdateHandler);
- }
-
- async render(visData, visParams, status) {
- if (status.params || (visParams.useTimeFilter && status.time)) {
- this.visParams = visParams;
- this.controls = [];
- this.controls = await this.initControls();
- this.drawVis();
- }
- }
-
- destroy() {
- this.updateSubsciption.unsubscribe();
- unmountComponentAtNode(this.el);
- this.controls.forEach(control => control.destroy());
- }
-
- drawVis = () => {
- render(
-
-
- ,
- this.el
- );
- };
-
- async initControls() {
- const controlParamsList = this.visParams.controls.filter(controlParams => {
- // ignore controls that do not have indexPattern or field
- return controlParams.indexPattern && controlParams.fieldName;
- });
-
- const controlFactoryPromises = controlParamsList.map(controlParams => {
- const factory = controlFactory(controlParams);
- return factory(controlParams, this.visParams.useTimeFilter, SearchSource);
- });
- const controls = await Promise.all(controlFactoryPromises);
-
- const getControl = id => {
- return controls.find(control => {
- return id === control.id;
- });
- };
-
- const controlInitPromises = [];
- getLineageMap(controlParamsList).forEach((lineage, controlId) => {
- // first lineage item is the control. remove it
- lineage.shift();
- const ancestors = [];
- lineage.forEach(ancestorId => {
- ancestors.push(getControl(ancestorId));
- });
- const control = getControl(controlId);
- control.setAncestors(ancestors);
- controlInitPromises.push(control.fetch());
- });
-
- await Promise.all(controlInitPromises);
- return controls;
- }
-
- stageFilter = async (controlIndex, newValue) => {
- this.controls[controlIndex].set(newValue);
- if (this.visParams.updateFiltersOnChange) {
- // submit filters on each control change
- this.submitFilters();
- } else {
- // Do not submit filters, just update vis so controls are updated with latest value
- await this.updateNestedControls();
- this.drawVis();
- }
- };
-
- submitFilters = () => {
- const stagedControls = this.controls.filter(control => {
- return control.hasChanged();
- });
-
- const newFilters = stagedControls
- .filter(control => {
- return control.hasKbnFilter();
- })
- .map(control => {
- return control.getKbnFilter();
- });
-
- stagedControls.forEach(control => {
- // to avoid duplicate filters, remove any old filters for control
- control.filterManager.findFilters().forEach(existingFilter => {
- this.filterManager.removeFilter(existingFilter);
- });
- });
-
- // Clean up filter pills for nested controls that are now disabled because ancestors are not set.
- // This has to be done after looking up the staged controls because otherwise removing a filter
- // will re-sync the controls of all other filters.
- this.controls.map(control => {
- if (control.hasAncestors() && control.hasUnsetAncestor()) {
- control.filterManager.findFilters().forEach(existingFilter => {
- this.filterManager.removeFilter(existingFilter);
- });
- }
- });
-
- this.filterManager.addFilters(newFilters, this.visParams.pinFilters);
- };
-
- clearControls = async () => {
- this.controls.forEach(control => {
- control.clear();
- });
- await this.updateNestedControls();
- this.drawVis();
- };
-
- updateControlsFromKbn = async () => {
- this.controls.forEach(control => {
- control.reset();
- });
- await this.updateNestedControls();
- this.drawVis();
- };
-
- async updateNestedControls() {
- const fetchPromises = this.controls.map(async control => {
- if (control.hasAncestors()) {
- await control.fetch();
- }
- });
- return await Promise.all(fetchPromises);
- }
-
- hasChanges = () => {
- return this.controls
- .map(control => {
- return control.hasChanged();
- })
- .reduce((a, b) => {
- return a || b;
- });
- };
-
- hasValues = () => {
- return this.controls
- .map(control => {
- return control.hasValue();
- })
- .reduce((a, b) => {
- return a || b;
- });
- };
-
- refreshControl = async (controlIndex, query) => {
- await this.controls[controlIndex].fetch(query);
- this.drawVis();
- };
-}
-
-export { VisController };
diff --git a/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx b/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx
new file mode 100644
index 0000000000000..849b58b8ee2da
--- /dev/null
+++ b/src/legacy/core_plugins/input_control_vis/public/vis_controller.tsx
@@ -0,0 +1,226 @@
+/*
+ * 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 React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+
+import { I18nStart } from 'kibana/public';
+import { Vis, VisParams, SearchSource } from './legacy_imports';
+
+import { InputControlVis } from './components/vis/input_control_vis';
+import { getControlFactory } from './control/control_factory';
+import { getLineageMap } from './lineage';
+import { ControlParams } from './editor_utils';
+import { RangeControl } from './control/range_control_factory';
+import { ListControl } from './control/list_control_factory';
+import { InputControlVisDependencies } from './plugin';
+import { FilterManager, esFilters } from '../../../../plugins/data/public';
+
+export const createInputControlVisController = (deps: InputControlVisDependencies) => {
+ return class InputControlVisController {
+ private I18nContext?: I18nStart['Context'];
+
+ controls: Array;
+ queryBarUpdateHandler: () => void;
+ filterManager: FilterManager;
+ updateSubsciption: any;
+ visParams?: VisParams;
+
+ constructor(public el: Element, public vis: Vis) {
+ this.controls = [];
+
+ this.queryBarUpdateHandler = this.updateControlsFromKbn.bind(this);
+
+ this.filterManager = deps.data.query.filterManager;
+ this.updateSubsciption = this.filterManager
+ .getUpdates$()
+ .subscribe(this.queryBarUpdateHandler);
+ }
+
+ async render(visData: any, visParams: VisParams, status: any) {
+ if (status.params || (visParams.useTimeFilter && status.time)) {
+ this.visParams = visParams;
+ this.controls = [];
+ this.controls = await this.initControls();
+ const [{ i18n }] = await deps.core.getStartServices();
+ this.I18nContext = i18n.Context;
+ this.drawVis();
+ }
+ }
+
+ destroy() {
+ this.updateSubsciption.unsubscribe();
+ unmountComponentAtNode(this.el);
+ this.controls.forEach(control => control.destroy());
+ }
+
+ drawVis = () => {
+ if (!this.I18nContext) {
+ throw new Error('no i18n context found');
+ }
+
+ render(
+
+
+ ,
+ this.el
+ );
+ };
+
+ async initControls() {
+ const controlParamsList = (this.visParams?.controls as ControlParams[])?.filter(
+ controlParams => {
+ // ignore controls that do not have indexPattern or field
+ return controlParams.indexPattern && controlParams.fieldName;
+ }
+ );
+
+ const controlFactoryPromises = controlParamsList.map(controlParams => {
+ const factory = getControlFactory(controlParams);
+ return factory(controlParams, this.visParams?.useTimeFilter, SearchSource, deps);
+ });
+ const controls = await Promise.all(controlFactoryPromises);
+
+ const getControl = (controlId: string) => {
+ return controls.find(({ id }) => id === controlId);
+ };
+
+ const controlInitPromises: Array> = [];
+ getLineageMap(controlParamsList).forEach((lineage, controlId) => {
+ // first lineage item is the control. remove it
+ lineage.shift();
+ const ancestors: Array = [];
+ lineage.forEach(ancestorId => {
+ const control = getControl(ancestorId);
+
+ if (control) {
+ ancestors.push(control);
+ }
+ });
+ const control = getControl(controlId);
+
+ if (control) {
+ control.setAncestors(ancestors);
+ controlInitPromises.push(control.fetch());
+ }
+ });
+
+ await Promise.all(controlInitPromises);
+ return controls;
+ }
+
+ stageFilter = async (controlIndex: number, newValue: any) => {
+ this.controls[controlIndex].set(newValue);
+ if (this.visParams?.updateFiltersOnChange) {
+ // submit filters on each control change
+ this.submitFilters();
+ } else {
+ // Do not submit filters, just update vis so controls are updated with latest value
+ await this.updateNestedControls();
+ this.drawVis();
+ }
+ };
+
+ submitFilters = () => {
+ const stagedControls = this.controls.filter(control => {
+ return control.hasChanged();
+ });
+
+ const newFilters = stagedControls
+ .map(control => control.getKbnFilter())
+ .filter((filter): filter is esFilters.Filter => {
+ return filter !== null;
+ });
+
+ stagedControls.forEach(control => {
+ // to avoid duplicate filters, remove any old filters for control
+ control.filterManager.findFilters().forEach(existingFilter => {
+ this.filterManager.removeFilter(existingFilter);
+ });
+ });
+
+ // Clean up filter pills for nested controls that are now disabled because ancestors are not set.
+ // This has to be done after looking up the staged controls because otherwise removing a filter
+ // will re-sync the controls of all other filters.
+ this.controls.map(control => {
+ if (control.hasAncestors() && control.hasUnsetAncestor()) {
+ control.filterManager.findFilters().forEach(existingFilter => {
+ this.filterManager.removeFilter(existingFilter);
+ });
+ }
+ });
+
+ this.filterManager.addFilters(newFilters, this.visParams?.pinFilters);
+ };
+
+ clearControls = async () => {
+ this.controls.forEach(control => {
+ control.clear();
+ });
+ await this.updateNestedControls();
+ this.drawVis();
+ };
+
+ updateControlsFromKbn = async () => {
+ this.controls.forEach(control => {
+ control.reset();
+ });
+ await this.updateNestedControls();
+ this.drawVis();
+ };
+
+ async updateNestedControls() {
+ const fetchPromises = this.controls.map(async control => {
+ if (control.hasAncestors()) {
+ await control.fetch();
+ }
+ });
+ return await Promise.all(fetchPromises);
+ }
+
+ hasChanges = () => {
+ return this.controls.map(control => control.hasChanged()).some(control => control);
+ };
+
+ hasValues = () => {
+ return this.controls
+ .map(control => {
+ return control.hasValue();
+ })
+ .reduce((a, b) => {
+ return a || b;
+ });
+ };
+
+ refreshControl = async (controlIndex: number, query: any) => {
+ await this.controls[controlIndex].fetch(query);
+ this.drawVis();
+ };
+ };
+};
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap
index 4ea658bcd03ef..178014a691be3 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap
+++ b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap
@@ -230,14 +230,18 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = `
type="dashboardApp"
>
+
+
+
+
+`;
+
+exports[`should render a Welcome screen with the telemetry disclaimer when optIn is false 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`should render a Welcome screen with the telemetry disclaimer when optIn is true 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/legacy/core_plugins/kibana/public/home/components/home.js b/src/legacy/core_plugins/kibana/public/home/components/home.js
index c87ceb9777c74..d552dd070c86d 100644
--- a/src/legacy/core_plugins/kibana/public/home/components/home.js
+++ b/src/legacy/core_plugins/kibana/public/home/components/home.js
@@ -51,10 +51,7 @@ export class Home extends Component {
getServices().getInjected('disableWelcomeScreen') ||
props.localStorage.getItem(KEY_ENABLE_WELCOME) === 'false'
);
- const showTelemetryDisclaimer = getServices().getInjected(
- 'telemetryNotifyUserAboutOptInDefault'
- );
-
+ const currentOptInStatus = this.props.getOptInStatus();
this.state = {
// If welcome is enabled, we wait for loading to complete
// before rendering. This prevents an annoying flickering
@@ -63,7 +60,7 @@ export class Home extends Component {
isLoading: isWelcomeEnabled,
isNewKibanaInstance: false,
isWelcomeEnabled,
- showTelemetryDisclaimer,
+ currentOptInStatus,
};
}
@@ -222,14 +219,13 @@ export class Home extends Component {
renderLoading() {
return '';
}
-
renderWelcome() {
return (
);
}
@@ -269,4 +265,5 @@ Home.propTypes = {
urlBasePath: PropTypes.string.isRequired,
mlEnabled: PropTypes.bool.isRequired,
onOptInSeen: PropTypes.func.isRequired,
+ getOptInStatus: PropTypes.func.isRequired,
};
diff --git a/src/legacy/core_plugins/kibana/public/home/components/home.test.js b/src/legacy/core_plugins/kibana/public/home/components/home.test.js
index 780e2af695381..1f46cf2875fee 100644
--- a/src/legacy/core_plugins/kibana/public/home/components/home.test.js
+++ b/src/legacy/core_plugins/kibana/public/home/components/home.test.js
@@ -63,6 +63,10 @@ describe('home', () => {
setItem: sinon.mock(),
},
urlBasePath: 'goober',
+ onOptInSeen() {
+ return false;
+ },
+ getOptInStatus: jest.fn(),
};
});
diff --git a/src/legacy/core_plugins/kibana/public/home/components/home_app.js b/src/legacy/core_plugins/kibana/public/home/components/home_app.js
index 5a12eb0a66cf1..29f24f5b841a3 100644
--- a/src/legacy/core_plugins/kibana/public/home/components/home_app.js
+++ b/src/legacy/core_plugins/kibana/public/home/components/home_app.js
@@ -29,14 +29,13 @@ import { getTutorial } from '../load_tutorials';
import { replaceTemplateStrings } from './tutorial/replace_template_strings';
import { getServices } from '../kibana_services';
import { npSetup } from 'ui/new_platform';
-
export function HomeApp({ directories }) {
const {
getInjected,
savedObjectsClient,
getBasePath,
addBasePath,
- telemetryOptInProvider: { setOptInNoticeSeen },
+ telemetryOptInProvider: { setOptInNoticeSeen, getOptIn },
} = getServices();
const { cloud } = npSetup.plugins;
const isCloudEnabled = !!(cloud && cloud.isCloudEnabled);
@@ -87,6 +86,7 @@ export function HomeApp({ directories }) {
localStorage={localStorage}
urlBasePath={getBasePath()}
onOptInSeen={setOptInNoticeSeen}
+ getOptInStatus={getOptIn}
/>
diff --git a/src/legacy/core_plugins/kibana/public/home/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap b/src/legacy/core_plugins/kibana/public/home/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap
index 07744dd0f6f4b..161061868dab2 100644
--- a/src/legacy/core_plugins/kibana/public/home/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/home/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap
@@ -508,6 +508,7 @@ exports[`bulkCreate should display success message when bulkCreate is successful
aria-label="complete"
className="euiIcon euiIcon--medium euiIcon-isLoaded euiStepNumber__icon"
focusable="false"
+ role="img"
style={null}
>
+
diff --git a/src/legacy/core_plugins/kibana/public/home/components/welcome.test.tsx b/src/legacy/core_plugins/kibana/public/home/components/welcome.test.tsx
index 21dcfd9ef15de..42c6e6ff6056a 100644
--- a/src/legacy/core_plugins/kibana/public/home/components/welcome.test.tsx
+++ b/src/legacy/core_plugins/kibana/public/home/components/welcome.test.tsx
@@ -35,7 +35,25 @@ jest.mock('../kibana_services', () => ({
test('should render a Welcome screen with the telemetry disclaimer', () => {
const component = shallow(
// @ts-ignore
- {}} showTelemetryDisclaimer={true} onOptInSeen={() => {}} />
+ {}} onOptInSeen={() => {}} />
+ );
+
+ expect(component).toMatchSnapshot();
+});
+
+test('should render a Welcome screen with the telemetry disclaimer when optIn is true', () => {
+ const component = shallow(
+ // @ts-ignore
+ {}} onOptInSeen={() => {}} currentOptInStatus={true} />
+ );
+
+ expect(component).toMatchSnapshot();
+});
+
+test('should render a Welcome screen with the telemetry disclaimer when optIn is false', () => {
+ const component = shallow(
+ // @ts-ignore
+ {}} onOptInSeen={() => {}} currentOptInStatus={false} />
);
expect(component).toMatchSnapshot();
@@ -45,7 +63,7 @@ test('should render a Welcome screen with no telemetry disclaimer', () => {
// @ts-ignore
const component = shallow(
// @ts-ignore
- {}} showTelemetryDisclaimer={false} onOptInSeen={() => {}} />
+ {}} onOptInSeen={() => {}} />
);
expect(component).toMatchSnapshot();
@@ -56,7 +74,7 @@ test('fires opt-in seen when mounted', () => {
shallow(
// @ts-ignore
- {}} showTelemetryDisclaimer={true} onOptInSeen={seen} />
+ {}} onOptInSeen={seen} />
);
expect(seen).toHaveBeenCalled();
diff --git a/src/legacy/core_plugins/kibana/public/home/components/welcome.tsx b/src/legacy/core_plugins/kibana/public/home/components/welcome.tsx
index c8de0bf7bb936..435bf98ca7840 100644
--- a/src/legacy/core_plugins/kibana/public/home/components/welcome.tsx
+++ b/src/legacy/core_plugins/kibana/public/home/components/welcome.tsx
@@ -23,7 +23,7 @@
* in Elasticsearch.
*/
-import React from 'react';
+import React, { Fragment } from 'react';
import {
EuiLink,
EuiTextColor,
@@ -39,12 +39,11 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { getServices } from '../kibana_services';
import { SampleDataCard } from './sample_data';
-
interface Props {
urlBasePath: string;
onSkip: () => void;
onOptInSeen: () => any;
- showTelemetryDisclaimer: boolean;
+ currentOptInStatus: boolean;
}
/**
@@ -84,9 +83,42 @@ export class Welcome extends React.Component {
document.removeEventListener('keydown', this.hideOnEsc);
}
- render() {
- const { urlBasePath, showTelemetryDisclaimer } = this.props;
+ private renderTelemetryEnabledOrDisabledText = () => {
+ if (this.props.currentOptInStatus) {
+ return (
+
+
+
+
+
+
+ );
+ } else {
+ return (
+
+
+
+
+
+
+ );
+ }
+ };
+ render() {
+ const { urlBasePath } = this.props;
return (
@@ -121,34 +153,23 @@ export class Welcome extends React.Component
{
onDecline={this.onSampleDataDecline}
/>
- {showTelemetryDisclaimer && (
-
-
-
-
-
+
+
+
-
-
-
-
- )}
+
+ {this.renderTelemetryEnabledOrDisabledText()}
+
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
index ca04ac8fcfaab..f758511990d6f 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
@@ -93,7 +93,6 @@ exports[`Table should render normally 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
@@ -136,9 +135,11 @@ exports[`Table should render the boolean template (false) 1`] = ` `;
exports[`Table should render the boolean template (true) 1`] = `
`;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
index f2a55649fe4d7..4716fb8f77633 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
@@ -55,7 +55,6 @@ exports[`Table should render normally 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/table/__jest__/__snapshots__/table.test.js.snap
index 415bae7389e97..7856572373e79 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/table/__jest__/__snapshots__/table.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/table/__jest__/__snapshots__/table.test.js.snap
@@ -78,7 +78,6 @@ exports[`Table should render normally 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index_pattern_table/index_pattern_table.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index_pattern_table/index_pattern_table.tsx
index f54afc4a5e359..f254a6bc22a0d 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index_pattern_table/index_pattern_table.tsx
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index_pattern_table/index_pattern_table.tsx
@@ -59,7 +59,7 @@ const columns = [
))}
),
- dataType: 'string',
+ dataType: 'string' as const,
sortable: ({ sort }: { sort: string }) => sort,
},
];
@@ -72,7 +72,7 @@ const pagination = {
const sorting = {
sort: {
field: 'title',
- direction: 'asc',
+ direction: 'asc' as const,
},
};
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap
index 843c8207c88c3..731a3379491c1 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap
@@ -54,7 +54,6 @@ exports[`ObjectsTable delete should show a confirm modal 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap
index a9175e7b2a63e..ace06e0420a7c 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap
@@ -90,7 +90,6 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
@@ -116,7 +115,6 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = `
}
}
responsive={true}
- sorting={false}
/>
@@ -411,7 +409,6 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
@@ -448,7 +445,6 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = `
}
}
responsive={true}
- sorting={false}
/>
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap
index 6060e96f3cfb6..941a0ffded820 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap
@@ -83,7 +83,6 @@ exports[`Relationships should render dashboards normally 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
@@ -155,7 +154,6 @@ exports[`Relationships should render dashboards normally 1`] = `
],
}
}
- sorting={false}
/>
@@ -294,7 +292,6 @@ exports[`Relationships should render index patterns normally 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
@@ -371,7 +368,6 @@ exports[`Relationships should render index patterns normally 1`] = `
],
}
}
- sorting={false}
/>
@@ -461,7 +457,6 @@ exports[`Relationships should render searches normally 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
@@ -538,7 +533,6 @@ exports[`Relationships should render searches normally 1`] = `
],
}
}
- sorting={false}
/>
@@ -628,7 +622,6 @@ exports[`Relationships should render visualizations normally 1`] = `
},
]
}
- executeQueryOptions={Object {}}
items={
Array [
Object {
@@ -700,7 +693,6 @@ exports[`Relationships should render visualizations normally 1`] = `
],
}
}
- sorting={false}
/>
diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts
index 8e414984a0c08..45cc1dc5fb9dd 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts
@@ -29,7 +29,7 @@ import { getTableAggs } from 'ui/visualize/loader/pipeline_helpers/utilities';
import { AppState } from 'ui/state_management/app_state';
import { npStart } from 'ui/new_platform';
import { IExpressionLoaderParams } from 'src/plugins/expressions/public';
-import { SearchSourceContract } from '../../../../../ui/public/courier';
+import { SearchSourceContract } from 'ui/courier';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
import {
IIndexPattern,
@@ -47,6 +47,7 @@ import {
APPLY_FILTER_TRIGGER,
} from '../../../../../../plugins/embeddable/public';
import { dispatchRenderComplete } from '../../../../../../plugins/kibana_utils/public';
+import { SavedSearch } from '../../discover/types';
const getKeys = (o: T): Array => Object.keys(o) as Array;
@@ -57,6 +58,10 @@ export interface VisSavedObject extends SavedObject {
title: string;
uiStateJSON?: string;
destroy: () => void;
+ savedSearchRefName?: string;
+ savedSearchId?: string;
+ savedSearch?: SavedSearch;
+ visState: any;
}
export interface VisualizeEmbeddableConfiguration {
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js
deleted file mode 100644
index fd643115e9084..0000000000000
--- a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.js
+++ /dev/null
@@ -1,150 +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.
- */
-
-/**
- * @name SavedVis
- *
- * @extends SavedObject.
- *
- * NOTE: It's a type of SavedObject, but specific to visualizations.
- */
-
-import { Vis } from 'ui/vis';
-import { uiModules } from 'ui/modules';
-import { updateOldState } from '../../../../visualizations/public';
-import { VisualizeConstants } from '../visualize_constants';
-import { createLegacyClass } from 'ui/utils/legacy_class';
-import { SavedObjectProvider } from 'ui/saved_objects/saved_object';
-import { extractReferences, injectReferences } from './saved_visualization_references';
-
-uiModules.get('app/visualize').factory('SavedVis', function(savedSearches, Private) {
- const SavedObject = Private(SavedObjectProvider);
- createLegacyClass(SavedVis).inherits(SavedObject);
- function SavedVis(opts) {
- const self = this;
- opts = opts || {};
- if (typeof opts !== 'object') opts = { id: opts };
-
- SavedVis.Super.call(self, {
- type: SavedVis.type,
- mapping: SavedVis.mapping,
- searchSource: SavedVis.searchSource,
- extractReferences: extractReferences,
- injectReferences: injectReferences,
-
- id: opts.id,
- indexPattern: opts.indexPattern,
- defaults: {
- title: '',
- visState: (function() {
- if (!opts.type) return null;
- const def = {};
- def.type = opts.type;
- return def;
- })(),
- uiStateJSON: '{}',
- description: '',
- savedSearchId: opts.savedSearchId,
- version: 1,
- },
-
- afterESResp: this._afterEsResp,
- });
-
- this.showInRecentlyAccessed = true;
- }
-
- SavedVis.type = 'visualization';
-
- SavedVis.mapping = {
- title: 'text',
- visState: 'json',
- uiStateJSON: 'text',
- description: 'text',
- savedSearchId: 'keyword',
- version: 'integer',
- };
-
- // Order these fields to the top, the rest are alphabetical
- SavedVis.fieldOrder = ['title', 'description'];
-
- SavedVis.searchSource = true;
-
- SavedVis.prototype.getFullPath = function() {
- return `/app/kibana#${VisualizeConstants.EDIT_PATH}/${this.id}`;
- };
-
- SavedVis.prototype._afterEsResp = async function() {
- const self = this;
-
- await self._getLinkedSavedSearch();
- self.searchSource.setField('size', 0);
- return self.vis ? self._updateVis() : self._createVis();
- };
-
- SavedVis.prototype._getLinkedSavedSearch = async function() {
- const self = this;
- const linkedSearch = !!self.savedSearchId;
- const current = self.savedSearch;
-
- if (linkedSearch && current && current.id === self.savedSearchId) {
- return;
- }
-
- if (self.savedSearch) {
- self.searchSource.setParent(self.savedSearch.searchSource.getParent());
- self.savedSearch.destroy();
- self.savedSearch = null;
- }
-
- if (linkedSearch) {
- self.savedSearch = await savedSearches.get(self.savedSearchId);
- self.searchSource.setParent(self.savedSearch.searchSource);
- }
- };
-
- SavedVis.prototype._createVis = function() {
- const self = this;
-
- self.visState = updateOldState(self.visState);
-
- // visState doesn't yet exist when importing a visualization, so we can't
- // assume that exists at this point. If it does exist, then we're not
- // importing a visualization, so we want to sync the title.
- if (self.visState) {
- self.visState.title = self.title;
- }
- self.vis = new Vis(self.searchSource.getField('index'), self.visState);
-
- self.vis.savedSearchId = self.savedSearchId;
-
- return self.vis;
- };
-
- SavedVis.prototype._updateVis = function() {
- const self = this;
-
- self.vis.indexPattern = self.searchSource.getField('index');
- self.visState.title = self.title;
- self.vis.setState(self.visState);
- self.vis.savedSearchId = self.savedSearchId;
- };
-
- return SavedVis;
-});
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.ts b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.ts
new file mode 100644
index 0000000000000..3490e0ab127ed
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/_saved_vis.ts
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+/**
+ * @name SavedVis
+ *
+ * @extends SavedObject.
+ *
+ * NOTE: It's a type of SavedObject, but specific to visualizations.
+ */
+// @ts-ignore
+import { Vis } from 'ui/vis';
+import { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types';
+import { createSavedObjectClass } from 'ui/saved_objects/saved_object';
+import { updateOldState } from '../../../../visualizations/public';
+import { VisualizeConstants } from '../visualize_constants';
+import { extractReferences, injectReferences } from './saved_visualization_references';
+import { IIndexPattern } from '../../../../../../plugins/data/public';
+import { VisSavedObject } from '../legacy_imports';
+
+import { createSavedSearchesService } from '../../discover';
+
+async function _afterEsResp(savedVis: VisSavedObject, services: any) {
+ await _getLinkedSavedSearch(savedVis, services);
+ savedVis.searchSource!.setField('size', 0);
+ savedVis.vis = savedVis.vis ? _updateVis(savedVis) : await _createVis(savedVis);
+ return savedVis;
+}
+
+async function _getLinkedSavedSearch(savedVis: VisSavedObject, services: any) {
+ const linkedSearch = !!savedVis.savedSearchId;
+ const current = savedVis.savedSearch;
+
+ if (linkedSearch && current && current.id === savedVis.savedSearchId) {
+ return;
+ }
+
+ if (savedVis.savedSearch) {
+ savedVis.searchSource!.setParent(savedVis.savedSearch.searchSource.getParent());
+ savedVis.savedSearch.destroy();
+ delete savedVis.savedSearch;
+ }
+ const savedSearches = createSavedSearchesService(services);
+
+ if (linkedSearch) {
+ savedVis.savedSearch = await savedSearches.get(savedVis.savedSearchId!);
+ savedVis.searchSource!.setParent(savedVis.savedSearch!.searchSource);
+ }
+}
+
+async function _createVis(savedVis: VisSavedObject) {
+ savedVis.visState = updateOldState(savedVis.visState);
+
+ // visState doesn't yet exist when importing a visualization, so we can't
+ // assume that exists at this point. If it does exist, then we're not
+ // importing a visualization, so we want to sync the title.
+ if (savedVis.visState) {
+ savedVis.visState.title = savedVis.title;
+ }
+ // the typescript compiler is wrong here, will be right when vis.js -> vis.ts
+ // @ts-ignore
+ savedVis.vis = new Vis(savedVis.searchSource!.getField('index'), savedVis.visState);
+
+ savedVis.vis!.savedSearchId = savedVis.savedSearchId;
+
+ return savedVis.vis;
+}
+
+function _updateVis(savedVis: VisSavedObject) {
+ if (savedVis.vis && savedVis.searchSource) {
+ savedVis.vis.indexPattern = savedVis.searchSource.getField('index');
+ savedVis.visState.title = savedVis.title;
+ savedVis.vis.setState(savedVis.visState);
+ savedVis.vis.savedSearchId = savedVis.savedSearchId;
+ }
+ return savedVis.vis;
+}
+
+export function createSavedVisClass(services: SavedObjectKibanaServices) {
+ const SavedObjectClass = createSavedObjectClass(services);
+
+ class SavedVis extends SavedObjectClass {
+ public static type: string = 'visualization';
+ public static mapping: Record = {
+ title: 'text',
+ visState: 'json',
+ uiStateJSON: 'text',
+ description: 'text',
+ savedSearchId: 'keyword',
+ version: 'integer',
+ };
+ // Order these fields to the top, the rest are alphabetical
+ public static fieldOrder = ['title', 'description'];
+ public static searchSource = true;
+
+ constructor(opts: Record | string = {}) {
+ if (typeof opts !== 'object') {
+ opts = { id: opts };
+ }
+ const visState = !opts.type ? null : { type: opts.type };
+ // Gives our SavedWorkspace the properties of a SavedObject
+ super({
+ type: SavedVis.type,
+ mapping: SavedVis.mapping,
+ searchSource: SavedVis.searchSource,
+ extractReferences,
+ injectReferences,
+ id: (opts.id as string) || '',
+ indexPattern: opts.indexPattern as IIndexPattern,
+ defaults: {
+ title: '',
+ visState,
+ uiStateJSON: '{}',
+ description: '',
+ savedSearchId: opts.savedSearchId,
+ version: 1,
+ },
+ afterESResp: (savedObject: SavedObject) => {
+ return _afterEsResp(savedObject as VisSavedObject, services) as Promise;
+ },
+ });
+ this.showInRecentlyAccessed = true;
+ this.getFullPath = () => {
+ return `/app/kibana#${VisualizeConstants.EDIT_PATH}/${this.id}`;
+ };
+ }
+ }
+
+ return SavedVis;
+}
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/index.js b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/index.js
rename to src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.test.js b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.test.ts
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.test.js
rename to src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.test.ts
index cdc232b06cf51..6549b317d1634 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.test.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.test.ts
@@ -18,6 +18,7 @@
*/
import { extractReferences, injectReferences } from './saved_visualization_references';
+import { VisSavedObject } from '../embeddable/visualize_embeddable';
describe('extractReferences', () => {
test('extracts nothing if savedSearchId is empty', () => {
@@ -26,6 +27,7 @@ describe('extractReferences', () => {
attributes: {
foo: true,
},
+ references: [],
};
const updatedDoc = extractReferences(doc);
expect(updatedDoc).toMatchInlineSnapshot(`
@@ -45,6 +47,7 @@ Object {
foo: true,
savedSearchId: '123',
},
+ references: [],
};
const updatedDoc = extractReferences(doc);
expect(updatedDoc).toMatchInlineSnapshot(`
@@ -83,6 +86,7 @@ Object {
},
}),
},
+ references: [],
};
const updatedDoc = extractReferences(doc);
@@ -108,13 +112,13 @@ describe('injectReferences', () => {
test('injects nothing when savedSearchRefName is null', () => {
const context = {
id: '1',
- foo: true,
- };
+ title: 'test',
+ } as VisSavedObject;
injectReferences(context, []);
expect(context).toMatchInlineSnapshot(`
Object {
- "foo": true,
"id": "1",
+ "title": "test",
}
`);
});
@@ -122,7 +126,7 @@ Object {
test('injects references into context', () => {
const context = {
id: '1',
- foo: true,
+ title: 'test',
savedSearchRefName: 'search_0',
visState: {
params: {
@@ -137,7 +141,7 @@ Object {
],
},
},
- };
+ } as VisSavedObject;
const references = [
{
name: 'search_0',
@@ -153,9 +157,9 @@ Object {
injectReferences(context, references);
expect(context).toMatchInlineSnapshot(`
Object {
- "foo": true,
"id": "1",
"savedSearchId": "123",
+ "title": "test",
"visState": Object {
"params": Object {
"controls": Array [
@@ -176,9 +180,9 @@ Object {
test(`fails when it can't find the saved search reference in the array`, () => {
const context = {
id: '1',
- foo: true,
savedSearchRefName: 'search_0',
- };
+ title: 'test',
+ } as VisSavedObject;
expect(() => injectReferences(context, [])).toThrowErrorMatchingInlineSnapshot(
`"Could not find saved search reference \\"search_0\\""`
);
@@ -187,6 +191,7 @@ Object {
test(`fails when it can't find the index pattern reference in the array`, () => {
const context = {
id: '1',
+ title: 'test',
visState: {
params: {
controls: [
@@ -197,7 +202,7 @@ Object {
],
},
},
- };
+ } as VisSavedObject;
expect(() => injectReferences(context, [])).toThrowErrorMatchingInlineSnapshot(
`"Could not find index pattern reference \\"control_0_index_pattern\\""`
);
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.js b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.ts
similarity index 77%
rename from src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.js
rename to src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.ts
index 7f451076e239c..dd8c2e9d2b74f 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_references.ts
@@ -16,8 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { SavedObjectAttributes, SavedObjectReference } from 'kibana/server';
+import { VisSavedObject } from '../embeddable/visualize_embeddable';
-export function extractReferences({ attributes, references = [] }) {
+export function extractReferences({
+ attributes,
+ references = [],
+}: {
+ attributes: SavedObjectAttributes;
+ references: SavedObjectReference[];
+}) {
const updatedAttributes = { ...attributes };
const updatedReferences = [...references];
@@ -26,7 +34,7 @@ export function extractReferences({ attributes, references = [] }) {
updatedReferences.push({
name: 'search_0',
type: 'search',
- id: updatedAttributes.savedSearchId,
+ id: String(updatedAttributes.savedSearchId),
});
delete updatedAttributes.savedSearchId;
updatedAttributes.savedSearchRefName = 'search_0';
@@ -34,9 +42,9 @@ export function extractReferences({ attributes, references = [] }) {
// Extract index patterns from controls
if (updatedAttributes.visState) {
- const visState = JSON.parse(updatedAttributes.visState);
+ const visState = JSON.parse(String(updatedAttributes.visState));
const controls = (visState.params && visState.params.controls) || [];
- controls.forEach((control, i) => {
+ controls.forEach((control: Record, i: number) => {
if (!control.indexPattern) {
return;
}
@@ -57,7 +65,7 @@ export function extractReferences({ attributes, references = [] }) {
};
}
-export function injectReferences(savedObject, references) {
+export function injectReferences(savedObject: VisSavedObject, references: SavedObjectReference[]) {
if (savedObject.savedSearchRefName) {
const savedSearchReference = references.find(
reference => reference.name === savedObject.savedSearchRefName
@@ -70,13 +78,11 @@ export function injectReferences(savedObject, references) {
}
if (savedObject.visState) {
const controls = (savedObject.visState.params && savedObject.visState.params.controls) || [];
- controls.forEach(control => {
+ controls.forEach((control: Record) => {
if (!control.indexPatternRefName) {
return;
}
- const reference = references.find(
- reference => reference.name === control.indexPatternRefName
- );
+ const reference = references.find(ref => ref.name === control.indexPatternRefName);
if (!reference) {
throw new Error(`Could not find index pattern reference "${control.indexPatternRefName}"`);
}
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_register.ts b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_register.ts
new file mode 100644
index 0000000000000..803474b1f7b3f
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualization_register.ts
@@ -0,0 +1,34 @@
+/*
+ * 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 { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry';
+// @ts-ignore
+import { savedObjectManagementRegistry } from '../../management/saved_object_registry';
+import './saved_visualizations';
+
+SavedObjectRegistryProvider.register((savedVisualizations: any) => {
+ return savedVisualizations;
+});
+
+// Register this service with the saved object registry so it can be
+// edited by the object editor.
+savedObjectManagementRegistry.register({
+ service: 'savedVisualizations',
+ title: 'visualizations',
+});
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualizations.js b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualizations.js
deleted file mode 100644
index 784f45436e3a3..0000000000000
--- a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualizations.js
+++ /dev/null
@@ -1,92 +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 './_saved_vis';
-import { uiModules } from 'ui/modules';
-import { SavedObjectLoader, SavedObjectsClientProvider } from 'ui/saved_objects';
-import { savedObjectManagementRegistry } from '../../management/saved_object_registry';
-import { start as visualizations } from '../../../../visualizations/public/np_ready/public/legacy';
-import { createVisualizeEditUrl } from '../visualize_constants';
-import { findListItems } from './find_list_items';
-import { npStart } from '../../../../../ui/public/new_platform';
-
-const app = uiModules.get('app/visualize');
-
-// Register this service with the saved object registry so it can be
-// edited by the object editor.
-savedObjectManagementRegistry.register({
- service: 'savedVisualizations',
- title: 'visualizations',
-});
-
-app.service('savedVisualizations', function(SavedVis, Private) {
- const visTypes = visualizations.types;
- const savedObjectClient = Private(SavedObjectsClientProvider);
- const saveVisualizationLoader = new SavedObjectLoader(
- SavedVis,
- savedObjectClient,
- npStart.core.chrome
- );
-
- saveVisualizationLoader.mapHitSource = function(source, id) {
- source.id = id;
- source.url = this.urlFor(id);
-
- let typeName = source.typeName;
- if (source.visState) {
- try {
- typeName = JSON.parse(source.visState).type;
- } catch (e) {
- /* missing typename handled below */
- } // eslint-disable-line no-empty
- }
-
- if (!typeName || !visTypes.get(typeName)) {
- source.error = 'Unknown visualization type';
- return source;
- }
-
- source.type = visTypes.get(typeName);
- source.savedObjectType = 'visualization';
- source.icon = source.type.icon;
- source.image = source.type.image;
- source.typeTitle = source.type.title;
- source.editUrl = `#${createVisualizeEditUrl(id)}`;
-
- return source;
- };
-
- saveVisualizationLoader.urlFor = function(id) {
- return `#/visualize/edit/${encodeURIComponent(id)}`;
- };
-
- // This behaves similarly to find, except it returns visualizations that are
- // defined as appExtensions and which may not conform to type: visualization
- saveVisualizationLoader.findListItems = function(search = '', size = 100) {
- return findListItems({
- search,
- size,
- mapSavedObjectApiHits: this.mapSavedObjectApiHits.bind(this),
- savedObjectsClient: this.savedObjectsClient,
- visTypes: visualizations.types.getAliases(),
- });
- };
-
- return saveVisualizationLoader;
-});
diff --git a/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualizations.ts b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualizations.ts
new file mode 100644
index 0000000000000..7425250bffe1a
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/visualize/saved_visualizations/saved_visualizations.ts
@@ -0,0 +1,86 @@
+/*
+ * 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 { npStart } from 'ui/new_platform';
+// @ts-ignore
+import { uiModules } from 'ui/modules';
+import { SavedObjectLoader } from 'ui/saved_objects';
+
+import { start as visualizations } from '../../../../visualizations/public/np_ready/public/legacy';
+import { createVisualizeEditUrl } from '../visualize_constants';
+// @ts-ignore
+import { findListItems } from './find_list_items';
+import { createSavedVisClass } from './_saved_vis';
+const app = uiModules.get('app/visualize');
+
+app.service('savedVisualizations', function() {
+ const savedObjectsClient = npStart.core.savedObjects.client;
+ const services = {
+ savedObjectsClient,
+ indexPatterns: npStart.plugins.data.indexPatterns,
+ chrome: npStart.core.chrome,
+ overlays: npStart.core.overlays,
+ };
+ class SavedObjectLoaderVisualize extends SavedObjectLoader {
+ mapHitSource = (source: Record, id: string) => {
+ const visTypes = visualizations.types;
+ source.id = id;
+ source.url = this.urlFor(id);
+
+ let typeName = source.typeName;
+ if (source.visState) {
+ try {
+ typeName = JSON.parse(String(source.visState)).type;
+ } catch (e) {
+ /* missing typename handled below */
+ } // eslint-disable-line no-empty
+ }
+
+ if (!typeName || !visTypes.get(typeName)) {
+ source.error = 'Unknown visualization type';
+ return source;
+ }
+
+ source.type = visTypes.get(typeName);
+ source.savedObjectType = 'visualization';
+ source.icon = source.type.icon;
+ source.image = source.type.image;
+ source.typeTitle = source.type.title;
+ source.editUrl = `#${createVisualizeEditUrl(id)}`;
+
+ return source;
+ };
+ urlFor(id: string) {
+ return `#/visualize/edit/${encodeURIComponent(id)}`;
+ }
+ // This behaves similarly to find, except it returns visualizations that are
+ // defined as appExtensions and which may not conform to type: visualization
+ findListItems(search: string = '', size: number = 100) {
+ return findListItems({
+ search,
+ size,
+ mapSavedObjectApiHits: this.mapSavedObjectApiHits.bind(this),
+ savedObjectsClient,
+ visTypes: visualizations.types.getAliases(),
+ });
+ }
+ }
+ const SavedVis = createSavedVisClass(services);
+
+ return new SavedObjectLoaderVisualize(SavedVis, savedObjectsClient, npStart.core.chrome);
+});
diff --git a/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap b/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap
index ca6b872c73f8f..0b44c7dc4e860 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap
+++ b/src/legacy/core_plugins/kibana/public/visualize/wizard/__snapshots__/new_vis_modal.test.tsx.snap
@@ -169,6 +169,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -226,6 +227,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiFormControlLayoutCustomIcon__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -275,6 +277,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiBetaBadge__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -290,6 +293,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -325,6 +329,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -361,6 +366,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -494,6 +500,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -551,6 +558,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiFormControlLayoutCustomIcon__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -600,6 +608,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiBetaBadge__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -615,6 +624,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -650,6 +660,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -686,6 +697,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -758,6 +770,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -815,6 +828,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiFormControlLayoutCustomIcon__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -864,6 +878,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--medium euiIcon-isLoading euiBetaBadge__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -879,6 +894,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -914,6 +930,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -950,6 +967,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiIcon euiIcon--large euiIcon--secondary euiIcon-isLoading"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
@@ -1031,16 +1049,18 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
type="cross"
>
);
diff --git a/src/legacy/ui/public/courier/search_source/types.ts b/src/legacy/ui/public/courier/search_source/types.ts
index 293f3d49596c3..8fd6d8cfa5fa0 100644
--- a/src/legacy/ui/public/courier/search_source/types.ts
+++ b/src/legacy/ui/public/courier/search_source/types.ts
@@ -17,8 +17,7 @@
* under the License.
*/
import { NameList } from 'elasticsearch';
-import { esFilters, Query } from '../../../../../plugins/data/public';
-import { IndexPattern } from '../../../../core_plugins/data/public/index_patterns';
+import { esFilters, Query, IndexPattern } from '../../../../../plugins/data/public';
export type EsQuerySearchAfter = [string | number, string | number];
@@ -47,6 +46,8 @@ export interface SearchSourceFields {
fields?: NameList;
index?: IndexPattern;
searchAfter?: EsQuerySearchAfter;
+ timeout?: string;
+ terminate_after?: number;
}
export interface SearchSourceOptions {
diff --git a/src/legacy/ui/public/directives/field_name/__snapshots__/field_name.test.tsx.snap b/src/legacy/ui/public/directives/field_name/__snapshots__/field_name.test.tsx.snap
index ff4258746db7a..bdb003771619c 100644
--- a/src/legacy/ui/public/directives/field_name/__snapshots__/field_name.test.tsx.snap
+++ b/src/legacy/ui/public/directives/field_name/__snapshots__/field_name.test.tsx.snap
@@ -6,10 +6,12 @@ exports[`FieldName renders a geo field, useShortDots is set to true 1`] = `
title="Geo point field"
>
Exit full screen
{ attributes: SavedObjectAttributes; references: SavedObjectReference[] };
@@ -34,7 +38,7 @@ export interface SavedObject {
getDisplayName: () => string;
getEsType: () => string;
getFullPath: () => string;
- hydrateIndexPattern?: (id?: string) => Promise;
+ hydrateIndexPattern?: (id?: string) => Promise;
id?: string;
init?: () => Promise;
isSaving: boolean;
@@ -66,7 +70,8 @@ export interface SavedObjectKibanaServices {
}
export interface SavedObjectConfig {
- afterESResp?: () => any;
+ // is only used by visualize
+ afterESResp?: (savedObject: SavedObject) => Promise;
clearSavedIndexPattern?: boolean;
defaults?: any;
extractReferences?: (opts: {
@@ -78,7 +83,7 @@ export interface SavedObjectConfig {
};
id?: string;
init?: () => void;
- indexPattern?: IndexPattern;
+ indexPattern?: IIndexPattern;
injectReferences?: any;
mapping?: any;
migrationVersion?: Record;
diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/__snapshots__/vislib_vis_legend.test.tsx.snap b/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/__snapshots__/vislib_vis_legend.test.tsx.snap
index f2c9f4e1b53ec..9b2b633fd0e16 100644
--- a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/__snapshots__/vislib_vis_legend.test.tsx.snap
+++ b/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/__snapshots__/vislib_vis_legend.test.tsx.snap
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`VisLegend Component Legend closed should match the snapshot 1`] = `""`;
+exports[`VisLegend Component Legend closed should match the snapshot 1`] = `""`;
-exports[`VisLegend Component Legend open should match the snapshot 1`] = `""`;
+exports[`VisLegend Component Legend open should match the snapshot 1`] = `""`;
diff --git a/src/plugins/data/common/es_query/filters/phrases_filter.ts b/src/plugins/data/common/es_query/filters/phrases_filter.ts
index 006e0623be913..2c964097e7c0f 100644
--- a/src/plugins/data/common/es_query/filters/phrases_filter.ts
+++ b/src/plugins/data/common/es_query/filters/phrases_filter.ts
@@ -42,7 +42,11 @@ export const getPhrasesFilterField = (filter: PhrasesFilter) => {
// Creates a filter where the given field matches one or more of the given values
// params should be an array of values
-export const buildPhrasesFilter = (field: IFieldType, params: any, indexPattern: IIndexPattern) => {
+export const buildPhrasesFilter = (
+ field: IFieldType,
+ params: any[],
+ indexPattern: IIndexPattern
+) => {
const index = indexPattern.id;
const type = FILTERS.PHRASES;
const key = field.name;
diff --git a/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts b/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts
index 0faf6f4a10346..60b2023f25609 100644
--- a/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts
+++ b/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts
@@ -18,8 +18,9 @@
*/
import { get } from 'lodash';
+import { IIndexPattern } from '../..';
-export function getFromSavedObject(savedObject: any) {
+export function getFromSavedObject(savedObject: any): IIndexPattern | undefined {
if (get(savedObject, 'attributes.fields') === undefined) {
return;
}
diff --git a/src/plugins/data/public/query/timefilter/get_time.ts b/src/plugins/data/public/query/timefilter/get_time.ts
index d3fbc17734f81..76f39da1cf706 100644
--- a/src/plugins/data/public/query/timefilter/get_time.ts
+++ b/src/plugins/data/public/query/timefilter/get_time.ts
@@ -18,10 +18,10 @@
*/
import dateMath from '@elastic/datemath';
-import { TimeRange } from '../../../common';
+import { IIndexPattern } from '../..';
+import { TimeRange, IFieldType } from '../../../common';
// TODO: remove this
-import { IndexPattern, Field } from '../../../../../legacy/core_plugins/data/public';
import { esFilters } from '../../../common';
interface CalculateBoundsOptions {
@@ -36,7 +36,7 @@ export function calculateBounds(timeRange: TimeRange, options: CalculateBoundsOp
}
export function getTime(
- indexPattern: IndexPattern | undefined,
+ indexPattern: IIndexPattern | undefined,
timeRange: TimeRange,
forceNow?: Date
) {
@@ -45,7 +45,7 @@ export function getTime(
return;
}
- const timefield: Field | undefined = indexPattern.fields.find(
+ const timefield: IFieldType | undefined = indexPattern.fields.find(
field => field.name === indexPattern.timeFieldName
);
diff --git a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
index ad453c4e5d11d..829c8205a8b52 100644
--- a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
+++ b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx
@@ -20,18 +20,21 @@
import _ from 'lodash';
import React, { Component } from 'react';
-import { EuiComboBox } from '@elastic/eui';
+import { Required } from '@kbn/utility-types';
+import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui';
+
import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../core/public';
import { getTitle } from '../../index_patterns/lib';
-export interface IndexPatternSelectProps {
- onChange: (opt: any) => void;
+export type IndexPatternSelectProps = Required<
+ Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>,
+ 'onChange' | 'placeholder'
+> & {
indexPatternId: string;
- placeholder: string;
- fieldTypes: string[];
- onNoIndexPatterns: () => void;
+ fieldTypes?: string[];
+ onNoIndexPatterns?: () => void;
savedObjectsClient: SavedObjectsClientContract;
-}
+};
interface IndexPatternSelectState {
isLoading: boolean;
@@ -136,7 +139,7 @@ export class IndexPatternSelect extends Component {
try {
const indexPatternFields = JSON.parse(savedObject.attributes.fields as any);
return indexPatternFields.some((field: any) => {
- return fieldTypes.includes(field.type);
+ return fieldTypes?.includes(field.type);
});
} catch (err) {
// Unable to parse fields JSON, invalid index pattern
@@ -196,6 +199,7 @@ export class IndexPatternSelect extends Component {
return (
{
options={this.state.options}
selectedOptions={this.state.selectedIndexPattern ? [this.state.selectedIndexPattern] : []}
onChange={this.onChange}
- {...rest}
/>
);
}
diff --git a/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap b/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap
index b865981768447..9cf725a2faa73 100644
--- a/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap
+++ b/src/plugins/inspector/public/ui/__snapshots__/inspector_panel.test.tsx.snap
@@ -271,16 +271,18 @@ exports[`InspectorPanel should render as expected 1`] = `
type="arrowDown"
>
string | number;
render: DataViewColumnRender;
}
-type DataViewColumnRender = (value: string) => string;
+type DataViewColumnRender = (value: string, _item: DataViewRow) => string;
export interface DataViewRow {
[fields: string]: {
diff --git a/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap b/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap
index 07e2dc7f35f80..942ca42dc6e85 100644
--- a/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap
+++ b/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap
@@ -33,14 +33,18 @@ exports[`is rendered 1`] = `
type="arrowLeft"
>
>} // EuiBasicTableColumn is stricter than Column
pagination={this.pagination}
loading={this.state.isFetchingItems}
message={noItemsMessage}
diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts
index ab686f4d5ffec..72bcc07ab98cf 100644
--- a/test/functional/services/browser.ts
+++ b/test/functional/services/browser.ts
@@ -30,13 +30,7 @@ import { Browsers } from './remote/browsers';
export async function BrowserProvider({ getService }: FtrProviderContext) {
const log = getService('log');
- const { driver, browserType, consoleLog$ } = await getService('__webdriver__').init();
-
- consoleLog$.subscribe(({ message, level }) => {
- log[level === 'SEVERE' || level === 'error' ? 'error' : 'debug'](
- `browser[${level}] ${message}`
- );
- });
+ const { driver, browserType } = await getService('__webdriver__').init();
const isW3CEnabled = (driver as any).executor_.w3c === true;
diff --git a/test/functional/services/remote/remote.ts b/test/functional/services/remote/remote.ts
index 90ff55fbebde5..69c2793621095 100644
--- a/test/functional/services/remote/remote.ts
+++ b/test/functional/services/remote/remote.ts
@@ -20,14 +20,11 @@
import Fs from 'fs';
import { resolve } from 'path';
-import * as Rx from 'rxjs';
import { mergeMap } from 'rxjs/operators';
-import { logging } from 'selenium-webdriver';
import { FtrProviderContext } from '../../ftr_provider_context';
import { initWebDriver } from './webdriver';
import { Browsers } from './browsers';
-import { pollForLogEntry$ } from './poll_for_log_entry';
export async function RemoteProvider({ getService }: FtrProviderContext) {
const lifecycle = getService('lifecycle');
@@ -37,7 +34,7 @@ export async function RemoteProvider({ getService }: FtrProviderContext) {
const collectCoverage: boolean = !!process.env.CODE_COVERAGE;
const coveragePrefix = 'coveragejson:';
const coverageDir = resolve(__dirname, '../../../../target/kibana-coverage/functional');
- let logSubscription: undefined | Rx.Subscription;
+ let coverageCounter = 1;
type BrowserStorage = 'sessionStorage' | 'localStorage';
const clearBrowserStorage = async (storageType: BrowserStorage) => {
@@ -50,6 +47,14 @@ export async function RemoteProvider({ getService }: FtrProviderContext) {
}
};
+ const writeCoverage = (coverageJson: string) => {
+ const id = coverageCounter++;
+ const timestamp = Date.now();
+ const path = resolve(coverageDir, `${id}.${timestamp}.coverage.json`);
+ log.info('writing coverage to', path);
+ Fs.writeFileSync(path, JSON.stringify(JSON.parse(coverageJson), null, 2));
+ };
+
const { driver, By, until, consoleLog$ } = await initWebDriver(
log,
browserType,
@@ -69,46 +74,35 @@ export async function RemoteProvider({ getService }: FtrProviderContext) {
caps.get('chrome').chromedriverVersion
}, w3c=${isW3CEnabled}, codeCoverage=${collectCoverage}`
);
-
- if (collectCoverage) {
- let coverageCounter = 1;
- // We are running xpack tests with different configs and cleanup will delete collected coverage
- // del.sync(coverageDir);
- Fs.mkdirSync(coverageDir, { recursive: true });
-
- logSubscription = pollForLogEntry$(
- driver,
- logging.Type.BROWSER,
- config.get('browser.logPollingMs'),
- lifecycle.cleanup.after$
- )
- .pipe(
- mergeMap(logEntry => {
- if (logEntry.message.includes(coveragePrefix)) {
- const id = coverageCounter++;
- const timestamp = Date.now();
- const path = resolve(coverageDir, `${id}.${timestamp}.coverage.json`);
- const [, coverageJsonBase64] = logEntry.message.split(coveragePrefix);
- const coverageJson = Buffer.from(coverageJsonBase64, 'base64').toString('utf8');
-
- log.info('writing coverage to', path);
- Fs.writeFileSync(path, JSON.stringify(JSON.parse(coverageJson), null, 2));
-
- // filter out this message
- return [];
- }
-
- return [logEntry];
- })
- )
- .subscribe({
- next({ message, level: { name: level } }) {
- const msg = message.replace(/\\n/g, '\n');
- log[level === 'SEVERE' ? 'error' : 'debug'](`browser[${level}] ${msg}`);
- },
- });
- }
}
+ // code coverage is supported only in Chrome browser
+ if (collectCoverage) {
+ // We are running xpack tests with different configs and cleanup will delete collected coverage
+ // del.sync(coverageDir);
+ Fs.mkdirSync(coverageDir, { recursive: true });
+ }
+
+ consoleLog$
+ .pipe(
+ mergeMap(logEntry => {
+ if (collectCoverage && logEntry.message.includes(coveragePrefix)) {
+ const [, coverageJsonBase64] = logEntry.message.split(coveragePrefix);
+ const coverageJson = Buffer.from(coverageJsonBase64, 'base64').toString('utf8');
+ writeCoverage(coverageJson);
+
+ // filter out this message
+ return [];
+ }
+
+ return [logEntry];
+ })
+ )
+ .subscribe({
+ next({ message, level }) {
+ const msg = message.replace(/\\n/g, '\n');
+ log[level === 'SEVERE' ? 'error' : 'debug'](`browser[${level}] ${msg}`);
+ },
+ });
lifecycle.beforeTests.add(async () => {
// hard coded default, can be overridden per suite using `browser.setWindowSize()`
@@ -144,8 +138,15 @@ export async function RemoteProvider({ getService }: FtrProviderContext) {
});
lifecycle.cleanup.add(async () => {
- if (logSubscription) {
- await new Promise(r => logSubscription!.add(r));
+ // Getting the last piece of code coverage before closing browser
+ if (collectCoverage) {
+ const coverageJson = await driver
+ .executeScript('return window.__coverage__')
+ .catch(() => undefined)
+ .then(coverage => coverage && JSON.stringify(coverage));
+ if (coverageJson) {
+ writeCoverage(coverageJson);
+ }
}
await driver.quit();
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
index ae4fa1578fb2b..9bd1a02f692ba 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
@@ -7,7 +7,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@elastic/eui": "17.0.0",
+ "@elastic/eui": "17.1.2",
"react": "^16.12.0",
"react-dom": "^16.12.0"
}
diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json
index 32da6eade2b42..a3ac52120d69b 100644
--- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json
+++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json
@@ -7,7 +7,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@elastic/eui": "17.0.0",
+ "@elastic/eui": "17.1.2",
"react": "^16.12.0"
}
}
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json
index 0ab65667d6f82..38fcc950adbe2 100644
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json
@@ -8,7 +8,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@elastic/eui": "17.0.0",
+ "@elastic/eui": "17.1.2",
"react": "^16.12.0"
},
"scripts": {
diff --git a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json
index 6f661ab2a79e1..fa8fa3775fa91 100644
--- a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json
+++ b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json
@@ -8,7 +8,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@elastic/eui": "17.0.0",
+ "@elastic/eui": "17.1.2",
"react": "^16.12.0"
},
"scripts": {
diff --git a/typings/@elastic/eui/index.d.ts b/typings/@elastic/eui/index.d.ts
index dc4d0d778fc55..716dbc075d798 100644
--- a/typings/@elastic/eui/index.d.ts
+++ b/typings/@elastic/eui/index.d.ts
@@ -25,17 +25,4 @@ declare module '@elastic/eui' {
export const EuiDescribedFormGroup: React.FC;
export const EuiCodeEditor: React.FC;
export const Query: any;
-
- export interface EuiTableCriteria {
- page: { index: number; size: number };
- sort?: {
- field?: string;
- direction?: Direction;
- };
- }
- export const EuiBasicTable: React.ComponentClass<{
- onTableChange?: (criteria: EuiTableCriteria) => void;
- sorting: { sort?: EuiTableCriteria['sort'] };
- [key: string]: any;
- }>;
}
diff --git a/x-pack/legacy/plugins/alerting/README.md b/x-pack/legacy/plugins/alerting/README.md
index 85dbd75e14174..0b4024be39548 100644
--- a/x-pack/legacy/plugins/alerting/README.md
+++ b/x-pack/legacy/plugins/alerting/README.md
@@ -200,7 +200,7 @@ Payload:
|name|A name to reference and search in the future.|string|
|tags|A list of keywords to reference and search in the future.|string[]|
|alertTypeId|The id value of the alert type you want to call when the alert is scheduled to execute.|string|
-|interval|The interval in seconds, minutes, hours or days the alert should execute. Example: `10s`, `5m`, `1h`, `1d`.|string|
+|schedule|The schedule specifying when this alert should run, using one of the available schedule formats specified under _Schedule Formats_ below|object|
|params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object|
|actions|Array of the following: - `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value. - `id` (string): The id of the action saved object to execute. - `params` (object): The map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array|
@@ -242,7 +242,7 @@ Payload:
|Property|Description|Type|
|---|---|---|
-|interval|The interval in seconds, minutes, hours or days the alert should execute. Example: `10s`, `5m`, `1h`, `1d`.|string|
+|schedule|The schedule specifying when this alert should be run, using one of the available schedule formats specified under _Schedule Formats_ below|object|
|name|A name to reference and search in the future.|string|
|tags|A list of keywords to reference and search in the future.|string[]|
|params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object|
@@ -304,6 +304,14 @@ Params:
|---|---|---|
|id|The id of the alert you're trying to update the API key for. System will use user in request context to generate an API key for.|string|
+##### Schedule Formats
+A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
+
+We currently support the _Interval format_ which specifies the interval in seconds, minutes, hours or days at which the alert should execute.
+Example: `{ interval: "10s" }`, `{ interval: "5m" }`, `{ interval: "1h" }`, `{ interval: "1d" }`.
+
+There are plans to support multiple other schedule formats in the near fuiture.
+
## Alert instance factory
**alertInstanceFactory(id)**
diff --git a/x-pack/legacy/plugins/alerting/mappings.json b/x-pack/legacy/plugins/alerting/mappings.json
index 7a7446602351d..bc4a7118666ed 100644
--- a/x-pack/legacy/plugins/alerting/mappings.json
+++ b/x-pack/legacy/plugins/alerting/mappings.json
@@ -13,7 +13,14 @@
"alertTypeId": {
"type": "keyword"
},
- "interval": {
+ "schedule": {
+ "properties": {
+ "interval": {
+ "type": "keyword"
+ }
+ }
+ },
+ "consumer": {
"type": "keyword"
},
"actions": {
diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts
index 37eb6a9b21d44..340b1bf766e24 100644
--- a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts
+++ b/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts
@@ -47,7 +47,8 @@ function getMockData(overwrites: Record = {}) {
name: 'abc',
tags: ['foo'],
alertTypeId: '123',
- interval: '10s',
+ consumer: 'bar',
+ schedule: { interval: '10s' },
throttle: null,
params: {
bar: true,
@@ -92,7 +93,7 @@ describe('create()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -157,10 +158,12 @@ describe('create()', () => {
],
"alertTypeId": "123",
"id": "1",
- "interval": "10s",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"scheduledTaskId": "task-123",
}
`);
@@ -182,15 +185,18 @@ describe('create()', () => {
"alertTypeId": "123",
"apiKey": undefined,
"apiKeyOwner": undefined,
+ "consumer": "bar",
"createdBy": "elastic",
"enabled": true,
- "interval": "10s",
"muteAll": false,
"mutedInstanceIds": Array [],
"name": "abc",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"tags": Array [
"foo",
],
@@ -298,7 +304,7 @@ describe('create()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -399,10 +405,12 @@ describe('create()', () => {
],
"alertTypeId": "123",
"id": "1",
- "interval": "10s",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"scheduledTaskId": "task-123",
}
`);
@@ -445,7 +453,7 @@ describe('create()', () => {
attributes: {
enabled: false,
alertTypeId: '123',
- interval: 10000,
+ schedule: { interval: 10000 },
params: {
bar: true,
},
@@ -484,10 +492,12 @@ describe('create()', () => {
"alertTypeId": "123",
"enabled": false,
"id": "1",
- "interval": 10000,
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": 10000,
+ },
}
`);
expect(savedObjectsClient.create).toHaveBeenCalledTimes(1);
@@ -585,7 +595,7 @@ describe('create()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -648,7 +658,7 @@ describe('create()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -722,7 +732,7 @@ describe('create()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -787,6 +797,7 @@ describe('create()', () => {
},
],
alertTypeId: '123',
+ consumer: 'bar',
name: 'abc',
params: { bar: true },
apiKey: Buffer.from('123:abc').toString('base64'),
@@ -794,7 +805,7 @@ describe('create()', () => {
createdBy: 'elastic',
updatedBy: 'elastic',
enabled: true,
- interval: '10s',
+ schedule: { interval: '10s' },
throttle: null,
muteAll: false,
mutedInstanceIds: [],
@@ -820,7 +831,7 @@ describe('enable()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: false,
},
@@ -846,7 +857,7 @@ describe('enable()', () => {
'alert',
'1',
{
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -879,7 +890,7 @@ describe('enable()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
},
@@ -897,7 +908,7 @@ describe('enable()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: false,
},
@@ -927,7 +938,7 @@ describe('enable()', () => {
'alert',
'1',
{
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -962,7 +973,7 @@ describe('disable()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -976,7 +987,7 @@ describe('disable()', () => {
'alert',
'1',
{
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
apiKey: null,
apiKeyOwner: null,
@@ -997,7 +1008,7 @@ describe('disable()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: false,
scheduledTaskId: 'task-123',
@@ -1060,7 +1071,7 @@ describe('muteInstance()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -1088,7 +1099,7 @@ describe('muteInstance()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -1107,7 +1118,7 @@ describe('muteInstance()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -1129,7 +1140,7 @@ describe('unmuteInstance()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -1157,7 +1168,7 @@ describe('unmuteInstance()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -1176,7 +1187,7 @@ describe('unmuteInstance()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
scheduledTaskId: 'task-123',
@@ -1199,7 +1210,7 @@ describe('get()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -1235,10 +1246,12 @@ describe('get()', () => {
],
"alertTypeId": "123",
"id": "1",
- "interval": "10s",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
}
`);
expect(savedObjectsClient.get).toHaveBeenCalledTimes(1);
@@ -1257,7 +1270,7 @@ describe('get()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -1292,7 +1305,7 @@ describe('find()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -1332,10 +1345,12 @@ describe('find()', () => {
],
"alertTypeId": "123",
"id": "1",
- "interval": "10s",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
},
],
"page": 1,
@@ -1362,7 +1377,7 @@ describe('delete()', () => {
type: 'alert',
attributes: {
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -1443,7 +1458,7 @@ describe('update()', () => {
type: 'alert',
attributes: {
enabled: true,
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -1470,7 +1485,7 @@ describe('update()', () => {
const result = await alertsClient.update({
id: '1',
data: {
- interval: '10s',
+ schedule: { interval: '10s' },
name: 'abc',
tags: ['foo'],
params: {
@@ -1501,10 +1516,12 @@ describe('update()', () => {
],
"enabled": true,
"id": "1",
- "interval": "10s",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"scheduledTaskId": "task-123",
}
`);
@@ -1528,11 +1545,13 @@ describe('update()', () => {
"apiKey": null,
"apiKeyOwner": null,
"enabled": true,
- "interval": "10s",
"name": "abc",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"scheduledTaskId": "task-123",
"tags": Array [
"foo",
@@ -1598,7 +1617,7 @@ describe('update()', () => {
type: 'alert',
attributes: {
enabled: true,
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -1651,7 +1670,7 @@ describe('update()', () => {
const result = await alertsClient.update({
id: '1',
data: {
- interval: '10s',
+ schedule: { interval: '10s' },
name: 'abc',
tags: ['foo'],
params: {
@@ -1712,10 +1731,12 @@ describe('update()', () => {
],
"enabled": true,
"id": "1",
- "interval": "10s",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"scheduledTaskId": "task-123",
}
`);
@@ -1771,7 +1792,7 @@ describe('update()', () => {
type: 'alert',
attributes: {
enabled: true,
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
@@ -1799,7 +1820,7 @@ describe('update()', () => {
const result = await alertsClient.update({
id: '1',
data: {
- interval: '10s',
+ schedule: { interval: '10s' },
name: 'abc',
tags: ['foo'],
params: {
@@ -1831,10 +1852,12 @@ describe('update()', () => {
"apiKey": "MTIzOmFiYw==",
"enabled": true,
"id": "1",
- "interval": "10s",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"scheduledTaskId": "task-123",
}
`);
@@ -1858,11 +1881,13 @@ describe('update()', () => {
"apiKey": "MTIzOmFiYw==",
"apiKeyOwner": "elastic",
"enabled": true,
- "interval": "10s",
"name": "abc",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"scheduledTaskId": "task-123",
"tags": Array [
"foo",
@@ -1909,7 +1934,7 @@ describe('update()', () => {
alertsClient.update({
id: '1',
data: {
- interval: '10s',
+ schedule: { interval: '10s' },
name: 'abc',
tags: ['foo'],
params: {
@@ -1939,7 +1964,7 @@ describe('updateApiKey()', () => {
id: '1',
type: 'alert',
attributes: {
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
},
@@ -1956,7 +1981,7 @@ describe('updateApiKey()', () => {
'alert',
'1',
{
- interval: '10s',
+ schedule: { interval: '10s' },
alertTypeId: '2',
enabled: true,
apiKey: Buffer.from('123:abc').toString('base64'),
diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.ts
index 27fda9871e685..578daa445b6ff 100644
--- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts
+++ b/x-pack/legacy/plugins/alerting/server/alerts_client.ts
@@ -8,7 +8,14 @@ import Boom from 'boom';
import { omit } from 'lodash';
import { i18n } from '@kbn/i18n';
import { Logger, SavedObjectsClientContract, SavedObjectReference } from 'src/core/server';
-import { Alert, RawAlert, AlertTypeRegistry, AlertAction, AlertType } from './types';
+import {
+ Alert,
+ RawAlert,
+ AlertTypeRegistry,
+ AlertAction,
+ AlertType,
+ IntervalSchedule,
+} from './types';
import { TaskManagerStartContract } from './shim';
import { validateAlertTypeParams } from './lib';
import { CreateAPIKeyResult as SecurityPluginCreateAPIKeyResult } from '../../../../plugins/security/server';
@@ -82,7 +89,7 @@ interface UpdateOptions {
data: {
name: string;
tags: string[];
- interval: string;
+ schedule: IntervalSchedule;
actions: NormalizedAlertAction[];
params: Record;
};
@@ -145,11 +152,7 @@ export class AlertsClient {
if (data.enabled) {
let scheduledTask;
try {
- scheduledTask = await this.scheduleAlert(
- createdAlert.id,
- rawAlert.alertTypeId,
- rawAlert.interval
- );
+ scheduledTask = await this.scheduleAlert(createdAlert.id, rawAlert.alertTypeId);
} catch (e) {
// Cleanup data, something went wrong scheduling the task
try {
@@ -259,11 +262,7 @@ export class AlertsClient {
const { attributes, version } = await this.savedObjectsClient.get('alert', id);
if (attributes.enabled === false) {
const apiKey = await this.createAPIKey();
- const scheduledTask = await this.scheduleAlert(
- id,
- attributes.alertTypeId,
- attributes.interval
- );
+ const scheduledTask = await this.scheduleAlert(id, attributes.alertTypeId);
const username = await this.getUserName();
await this.savedObjectsClient.update(
'alert',
@@ -364,7 +363,7 @@ export class AlertsClient {
}
}
- private async scheduleAlert(id: string, alertTypeId: string, interval: string) {
+ private async scheduleAlert(id: string, alertTypeId: string) {
return await this.taskManager.schedule({
taskType: `alerting:${alertTypeId}`,
params: {
diff --git a/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.test.ts b/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.test.ts
index 852e412689b35..1c4d8a42d2830 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.test.ts
+++ b/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.test.ts
@@ -15,12 +15,12 @@ const mockedNow = new Date('2019-06-03T18:55:25.982Z');
test('Adds interface to given date when result is > Date.now()', () => {
const currentRunAt = new Date('2019-06-03T18:55:23.982Z');
- const result = getNextRunAt(currentRunAt, '10s');
+ const result = getNextRunAt(currentRunAt, { interval: '10s' });
expect(result).toEqual(new Date('2019-06-03T18:55:33.982Z'));
});
test('Uses Date.now() when the result would of been a date in the past', () => {
const currentRunAt = new Date('2019-06-03T18:55:13.982Z');
- const result = getNextRunAt(currentRunAt, '10s');
+ const result = getNextRunAt(currentRunAt, { interval: '10s' });
expect(result).toEqual(mockedNow);
});
diff --git a/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts b/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts
index 901b614b4d68c..f9867b5372908 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts
+++ b/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts
@@ -5,9 +5,10 @@
*/
import { parseDuration } from './parse_duration';
+import { IntervalSchedule } from '../types';
-export function getNextRunAt(currentRunAt: Date, interval: string) {
- let nextRunAt = currentRunAt.getTime() + parseDuration(interval);
+export function getNextRunAt(currentRunAt: Date, schedule: IntervalSchedule) {
+ let nextRunAt = currentRunAt.getTime() + parseDuration(schedule.interval);
if (nextRunAt < Date.now()) {
// To prevent returning dates in the past, we'll return now instead
nextRunAt = Date.now();
diff --git a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts
index c21c419977bbe..7966f98c749c8 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts
+++ b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts
@@ -74,7 +74,7 @@ const mockedAlertTypeSavedObject = {
attributes: {
enabled: true,
alertTypeId: '123',
- interval: '10s',
+ schedule: { interval: '10s' },
mutedInstanceIds: [],
params: {
bar: true,
diff --git a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts
index 051b15fc8dd8f..fe0979538d04e 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts
+++ b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts
@@ -20,6 +20,7 @@ import {
GetServicesFunction,
RawAlert,
SpaceIdToNamespaceFunction,
+ IntervalSchedule,
} from '../types';
export interface TaskRunnerContext {
@@ -94,7 +95,7 @@ export class TaskRunnerFactory {
const services = getServices(fakeRequest);
// Ensure API key is still valid and user has access
const {
- attributes: { params, actions, interval, throttle, muteAll, mutedInstanceIds },
+ attributes: { params, actions, schedule, throttle, muteAll, mutedInstanceIds },
references,
} = await services.savedObjectsClient.get('alert', alertId);
@@ -167,7 +168,13 @@ export class TaskRunnerFactory {
})
);
- const nextRunAt = getNextRunAt(new Date(taskInstance.startedAt!), interval);
+ const nextRunAt = getNextRunAt(
+ new Date(taskInstance.startedAt!),
+ // we do not currently have a good way of returning the type
+ // from SavedObjectsClient, and as we currenrtly require a schedule
+ // and we only support `interval`, we can cast this safely
+ schedule as IntervalSchedule
+ );
return {
state: {
diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts
index 634a797880812..c41e0d068aff2 100644
--- a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts
+++ b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts
@@ -12,8 +12,9 @@ server.route(createAlertRoute);
const mockedAlert = {
alertTypeId: '1',
+ consumer: 'bar',
name: 'abc',
- interval: '10s',
+ schedule: { interval: '10s' },
tags: ['foo'],
params: {
bar: true,
@@ -64,12 +65,15 @@ test('creates an alert with proper parameters', async () => {
},
],
"alertTypeId": "1",
+ "consumer": "bar",
"id": "123",
- "interval": "10s",
"name": "abc",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"tags": Array [
"foo",
],
@@ -90,12 +94,15 @@ test('creates an alert with proper parameters', async () => {
},
],
"alertTypeId": "1",
+ "consumer": "bar",
"enabled": true,
- "interval": "10s",
"name": "abc",
"params": Object {
"bar": true,
},
+ "schedule": Object {
+ "interval": "10s",
+ },
"tags": Array [
"foo",
],
diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.ts b/x-pack/legacy/plugins/alerting/server/routes/create.ts
index cb5277ae19100..362a23a3fa910 100644
--- a/x-pack/legacy/plugins/alerting/server/routes/create.ts
+++ b/x-pack/legacy/plugins/alerting/server/routes/create.ts
@@ -7,6 +7,7 @@
import Hapi from 'hapi';
import Joi from 'joi';
import { getDurationSchema } from '../lib';
+import { IntervalSchedule } from '../types';
interface ScheduleRequest extends Hapi.Request {
payload: {
@@ -14,7 +15,8 @@ interface ScheduleRequest extends Hapi.Request {
name: string;
tags: string[];
alertTypeId: string;
- interval: string;
+ consumer: string;
+ schedule: IntervalSchedule;
actions: Array<{
group: string;
id: string;
@@ -42,8 +44,13 @@ export const createAlertRoute = {
.items(Joi.string())
.default([]),
alertTypeId: Joi.string().required(),
+ consumer: Joi.string().required(),
throttle: getDurationSchema().default(null),
- interval: getDurationSchema().required(),
+ schedule: Joi.object()
+ .keys({
+ interval: getDurationSchema().required(),
+ })
+ .required(),
params: Joi.object().required(),
actions: Joi.array()
.items(
diff --git a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts b/x-pack/legacy/plugins/alerting/server/routes/get.test.ts
index 4d44ee9dfe6bd..b97762d10c960 100644
--- a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts
+++ b/x-pack/legacy/plugins/alerting/server/routes/get.test.ts
@@ -13,7 +13,7 @@ server.route(getAlertRoute);
const mockedAlert = {
id: '1',
alertTypeId: '1',
- interval: '10s',
+ schedule: { interval: '10s' },
params: {
bar: true,
},
diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts b/x-pack/legacy/plugins/alerting/server/routes/update.test.ts
index 334fb2120319d..8ce9d94140e6d 100644
--- a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts
+++ b/x-pack/legacy/plugins/alerting/server/routes/update.test.ts
@@ -16,7 +16,7 @@ const mockedResponse = {
id: '1',
alertTypeId: '1',
tags: ['foo'],
- interval: '12s',
+ schedule: { interval: '12s' },
params: {
otherField: false,
},
@@ -40,7 +40,7 @@ test('calls the update function with proper parameters', async () => {
throttle: null,
name: 'abc',
tags: ['bar'],
- interval: '12s',
+ schedule: { interval: '12s' },
params: {
otherField: false,
},
@@ -75,11 +75,13 @@ test('calls the update function with proper parameters', async () => {
},
},
],
- "interval": "12s",
"name": "abc",
"params": Object {
"otherField": false,
},
+ "schedule": Object {
+ "interval": "12s",
+ },
"tags": Array [
"bar",
],
diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.ts b/x-pack/legacy/plugins/alerting/server/routes/update.ts
index 6e8f8557fb24a..bc55d48465602 100644
--- a/x-pack/legacy/plugins/alerting/server/routes/update.ts
+++ b/x-pack/legacy/plugins/alerting/server/routes/update.ts
@@ -7,6 +7,7 @@
import Joi from 'joi';
import Hapi from 'hapi';
import { getDurationSchema } from '../lib';
+import { IntervalSchedule } from '../types';
interface UpdateRequest extends Hapi.Request {
params: {
@@ -16,7 +17,7 @@ interface UpdateRequest extends Hapi.Request {
alertTypeId: string;
name: string;
tags: string[];
- interval: string;
+ schedule: IntervalSchedule;
actions: Array<{
group: string;
id: string;
@@ -45,7 +46,11 @@ export const updateAlertRoute = {
tags: Joi.array()
.items(Joi.string())
.required(),
- interval: getDurationSchema().required(),
+ schedule: Joi.object()
+ .keys({
+ interval: getDurationSchema().required(),
+ })
+ .required(),
params: Joi.object().required(),
actions: Joi.array()
.items(
diff --git a/x-pack/legacy/plugins/alerting/server/types.ts b/x-pack/legacy/plugins/alerting/server/types.ts
index 1bec2632d8082..f11c36adbeb64 100644
--- a/x-pack/legacy/plugins/alerting/server/types.ts
+++ b/x-pack/legacy/plugins/alerting/server/types.ts
@@ -60,12 +60,17 @@ export interface RawAlertAction extends SavedObjectAttributes {
params: AlertActionParams;
}
+export interface IntervalSchedule extends SavedObjectAttributes {
+ interval: string;
+}
+
export interface Alert {
enabled: boolean;
name: string;
tags: string[];
alertTypeId: string;
- interval: string;
+ consumer: string;
+ schedule: IntervalSchedule;
actions: AlertAction[];
params: Record;
scheduledTaskId?: string;
@@ -83,7 +88,8 @@ export interface RawAlert extends SavedObjectAttributes {
name: string;
tags: string[];
alertTypeId: string;
- interval: string;
+ consumer: string;
+ schedule: SavedObjectAttributes;
actions: RawAlertAction[];
params: SavedObjectAttributes;
scheduledTaskId?: string;
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap
index 2b053274e8afe..9358eeceb1a44 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap
@@ -93,10 +93,11 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = `
className="euiButtonEmpty__content"
>
List should render empty state 1`] = `
Latest occurrence
List should render empty state 1`] = `
-
`;
@@ -472,10 +452,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
className="euiButtonEmpty__content"
>
List should render with data 1`] = `
Latest occurrence
List should render with data 1`] = `
-
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
index 489d4f2908cbe..9b2a2c8f2490a 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
@@ -111,12 +111,14 @@ NodeList [
class="euiIcon euiIcon--medium euiIcon-isLoaded euiButton__icon"
focusable="false"
height="16"
+ role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
+
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx
index 53c66e04c468a..c8404b02afe70 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiBasicTable } from '@elastic/eui';
+import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui';
import { sortByOrder } from 'lodash';
import React, { useMemo, useCallback, ReactNode } from 'react';
import { useUrlParams } from '../../../hooks/useUrlParams';
@@ -69,8 +69,8 @@ function UnoptimizedManagedTable(props: Props) {
const sort = useMemo(() => {
return {
sort: {
- field: sortField,
- direction: sortDirection
+ field: sortField as keyof T,
+ direction: sortDirection as 'asc' | 'desc'
}
};
}, [sortField, sortDirection]);
@@ -78,7 +78,7 @@ function UnoptimizedManagedTable(props: Props) {
const onTableChange = useCallback(
(options: {
page: { index: number; size: number };
- sort: { field: string; direction: 'asc' | 'desc' };
+ sort?: { field: keyof T; direction: 'asc' | 'desc' };
}) => {
history.push({
...history.location,
@@ -86,8 +86,8 @@ function UnoptimizedManagedTable(props: Props) {
...toQuery(history.location.search),
page: options.page.index,
pageSize: options.page.size,
- sortField: options.sort.field,
- sortDirection: options.sort.direction
+ sortField: options.sort!.field,
+ sortDirection: options.sort!.direction
})
});
},
@@ -107,7 +107,7 @@ function UnoptimizedManagedTable(props: Props) {
>} // EuiBasicTableColumn is stricter than ITableColumn
pagination={pagination}
sorting={sort}
onChange={onTableChange}
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap
index 5b17b124a321d..ea1b825c856ad 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap
@@ -185,14 +185,18 @@ exports[`Stackframe when stackframe has source lines should render correctly 1`]
type="arrowRight"
>
+
diff --git a/x-pack/legacy/plugins/apm/readme.md b/x-pack/legacy/plugins/apm/readme.md
index 0f928fe626bd3..6b21f08b7695e 100644
--- a/x-pack/legacy/plugins/apm/readme.md
+++ b/x-pack/legacy/plugins/apm/readme.md
@@ -31,11 +31,8 @@ _Docker Compose is required_
### Setup default APM users
-APM behaves differently depending on which the role and permissions a logged in user has.
-For testing purposes APM has invented 4 custom users:
-
-
-**elastic**: Apps: read/write. Indices: read/write (all)
+APM behaves differently depending on which the role and permissions a logged in user has.
+For testing purposes APM uses 3 custom users:
**apm_read_user**: Apps: read. Indices: read (`apm-*`)
@@ -44,10 +41,10 @@ For testing purposes APM has invented 4 custom users:
**kibana_write_user** Apps: read/write. Indices: None
-To create the 4 users with the correct roles run the following script:
+To create the users with the correct roles run the following script:
```sh
-node x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js --username
+node x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js --role-suffix
```
The users will be created with the password specified in kibana.dev.yml for `elasticsearch.password`
diff --git a/x-pack/legacy/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts b/x-pack/legacy/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts
index 66f2a8d1ac79f..85aa43f78f7dd 100644
--- a/x-pack/legacy/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts
+++ b/x-pack/legacy/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts
@@ -20,13 +20,13 @@ const config = yaml.safeLoad(
)
);
-const GITHUB_USERNAME = argv.username as string;
const KIBANA_INDEX = config['kibana.index'] as string;
const TASK_MANAGER_INDEX = config['xpack.task_manager.index'] as string;
-const ELASTICSEARCH_USERNAME = (argv.esUsername as string) || 'elastic';
-const ELASTICSEARCH_PASSWORD = (argv.esPassword ||
+const KIBANA_ROLE_SUFFIX = argv.roleSuffix as string;
+const ELASTICSEARCH_USERNAME = (argv.username as string) || 'elastic';
+const ELASTICSEARCH_PASSWORD = (argv.password ||
config['elasticsearch.password']) as string;
-const KIBANA_BASE_URL = (argv.baseUrl as string) || 'http://localhost:5601';
+const KIBANA_BASE_URL = (argv.kibanaUrl as string) || 'http://localhost:5601';
interface User {
username: string;
@@ -40,51 +40,76 @@ const getKibanaBasePath = once(async () => {
try {
await axios.request({ url: KIBANA_BASE_URL, maxRedirects: 0 });
} catch (e) {
- const err = e as AxiosError;
- const { location } = err.response?.headers;
- const isBasePath = RegExp(/^\/\w{3}$/).test(location);
- return isBasePath ? location : '';
+ if (isAxiosError(e)) {
+ const location = e.response?.headers?.location;
+ const isBasePath = RegExp(/^\/\w{3}$/).test(location);
+ return isBasePath ? location : '';
+ }
+
+ throw e;
}
return '';
});
init().catch(e => {
- if (e.response) {
- console.log(
- JSON.stringify({ request: e.config, response: e.response.data }, null, 2)
+ if (e instanceof AbortError) {
+ console.error(e.message);
+ } else if (isAxiosError(e)) {
+ console.error(
+ `${e.config.method?.toUpperCase() || 'GET'} ${e.config.url} (Code: ${
+ e.response?.status
+ })`
);
- return;
- }
- console.log(e);
+ if (e.response) {
+ console.error(
+ JSON.stringify(
+ { request: e.config, response: e.response.data },
+ null,
+ 2
+ )
+ );
+ }
+ } else {
+ console.error(e);
+ }
});
async function init() {
+ const version = await getKibanaVersion();
+ console.log(`Connected to Kibana ${version}`);
+
+ const isKibanaLocal = KIBANA_BASE_URL.startsWith('http://localhost');
+
// kibana.index must be different from `.kibana`
- if (KIBANA_INDEX === '.kibana') {
+ if (isKibanaLocal && KIBANA_INDEX === '.kibana') {
console.log(
'kibana.dev.yml: Please use a custom "kibana.index". Example: "kibana.index: .kibana-john"'
);
return;
}
- if (!KIBANA_INDEX.startsWith('.kibana')) {
+ if (isKibanaLocal && !KIBANA_INDEX.startsWith('.kibana')) {
console.log(
'kibana.dev.yml: "kibana.index" must be prefixed with `.kibana`. Example: "kibana.index: .kibana-john"'
);
return;
}
- if (TASK_MANAGER_INDEX && !TASK_MANAGER_INDEX.startsWith('.kibana')) {
+ if (
+ isKibanaLocal &&
+ TASK_MANAGER_INDEX &&
+ !TASK_MANAGER_INDEX.startsWith('.kibana')
+ ) {
console.log(
'kibana.dev.yml: "xpack.task_manager.index" must be prefixed with `.kibana`. Example: "xpack.task_manager.index: .kibana-task-manager-john"'
);
return;
}
- if (!GITHUB_USERNAME) {
+ if (!KIBANA_ROLE_SUFFIX) {
console.log(
- 'Please specify your github username with `--username ` '
+ 'Please specify a unique suffix that will be added to your roles with `--role-suffix ` '
);
return;
}
@@ -95,8 +120,8 @@ async function init() {
return;
}
- const KIBANA_READ_ROLE = `kibana_read_${GITHUB_USERNAME}`;
- const KIBANA_WRITE_ROLE = `kibana_write_${GITHUB_USERNAME}`;
+ const KIBANA_READ_ROLE = `kibana_read_${KIBANA_ROLE_SUFFIX}`;
+ const KIBANA_WRITE_ROLE = `kibana_write_${KIBANA_ROLE_SUFFIX}`;
// create roles
await createRole({ roleName: KIBANA_READ_ROLE, privilege: 'read' });
@@ -132,16 +157,18 @@ async function isSecurityEnabled() {
}
async function callKibana(options: AxiosRequestConfig): Promise {
- const basePath = await getKibanaBasePath();
- const { data } = await axios.request({
+ const kibanaBasePath = await getKibanaBasePath();
+ const reqOptions = {
...options,
- baseURL: KIBANA_BASE_URL + basePath,
+ baseURL: KIBANA_BASE_URL + kibanaBasePath,
auth: {
username: ELASTICSEARCH_USERNAME,
password: ELASTICSEARCH_PASSWORD
},
headers: { 'kbn-xsrf': 'true', ...options.headers }
- });
+ };
+
+ const { data } = await axios.request(reqOptions);
return data;
}
@@ -222,10 +249,8 @@ async function getUser(username: string) {
url: `/internal/security/users/${username}`
});
} catch (e) {
- const err = e as AxiosError;
-
// return empty if user doesn't exist
- if (err.response?.status === 404) {
+ if (isAxiosError(e) && e.response?.status === 404) {
return null;
}
@@ -240,13 +265,51 @@ async function getRole(roleName: string) {
url: `/api/security/role/${roleName}`
});
} catch (e) {
- const err = e as AxiosError;
-
// return empty if role doesn't exist
- if (err.response?.status === 404) {
+ if (isAxiosError(e) && e.response?.status === 404) {
return null;
}
throw e;
}
}
+
+async function getKibanaVersion() {
+ try {
+ const res: { version: { number: number } } = await callKibana({
+ method: 'GET',
+ url: `/api/status`
+ });
+ return res.version.number;
+ } catch (e) {
+ if (isAxiosError(e)) {
+ switch (e.response?.status) {
+ case 401:
+ throw new AbortError(
+ `Could not access Kibana with the provided credentials. Username: "${e.config.auth?.username}". Password: "${e.config.auth?.password}"`
+ );
+
+ case 404:
+ throw new AbortError(
+ `Could not get version on ${e.config.url} (Code: 404)`
+ );
+
+ default:
+ throw new AbortError(
+ `Cannot access Kibana on ${e.config.baseURL}. Please specify Kibana with: "--kibana-url "`
+ );
+ }
+ }
+ throw e;
+ }
+}
+
+function isAxiosError(e: AxiosError | Error): e is AxiosError {
+ return 'isAxiosError' in e;
+}
+
+class AbortError extends Error {
+ constructor(message: string) {
+ super(message);
+ }
+}
diff --git a/x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js b/x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js
index a3dc3d66f56a0..825c1a526fcc5 100644
--- a/x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js
+++ b/x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js
@@ -12,7 +12,7 @@
* The two roles will be assigned to the already existing users: `apm_read_user`, `apm_write_user`, `kibana_write_user`
*
* This makes it possible to use the existing cloud users locally
- * Usage: node setup-kibana-security.js --username YOUR-GITHUB-USERNAME
+ * Usage: node setup-kibana-security.js --role-suffix
******************************/
// compile typescript on the fly
diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/table.tsx b/x-pack/legacy/plugins/beats_management/public/components/table/table.tsx
index 8b101196d21ee..26ddd682405cb 100644
--- a/x-pack/legacy/plugins/beats_management/public/components/table/table.tsx
+++ b/x-pack/legacy/plugins/beats_management/public/components/table/table.tsx
@@ -93,7 +93,7 @@ export class Table extends React.Component {
};
const selectionOptions = hideTableControls
- ? null
+ ? undefined
: {
onSelectionChange: this.setSelection,
selectable: () => true,
@@ -148,7 +148,7 @@ export class Table extends React.Component {
);
}
- private onTableChange = (page: { index: number; size: number } = { index: 0, size: 50 }) => {
+ private onTableChange = ({ page }: { page: { index: number; size: number } }) => {
if (this.props.onTableChange) {
this.props.onTableChange(page.index, page.size);
}
diff --git a/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx b/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx
index 6f03f884563e1..6fff7574c39e6 100644
--- a/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx
+++ b/x-pack/legacy/plugins/beats_management/public/components/table/table_type_configs.tsx
@@ -14,7 +14,7 @@ import { ConnectedLink } from '../navigation/connected_link';
import { TagBadge } from '../tag';
export interface ColumnDefinition {
- align?: string;
+ align?: 'left' | 'right' | 'center' | undefined;
field: string;
name: string;
sortable?: boolean;
diff --git a/x-pack/legacy/plugins/beats_management/public/pages/beat/details.tsx b/x-pack/legacy/plugins/beats_management/public/pages/beat/details.tsx
index 3eaf550cb8c77..3952b44f82561 100644
--- a/x-pack/legacy/plugins/beats_management/public/pages/beat/details.tsx
+++ b/x-pack/legacy/plugins/beats_management/public/pages/beat/details.tsx
@@ -13,6 +13,7 @@ import {
EuiSpacer,
EuiText,
EuiTitle,
+ EuiBasicTableColumn,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
@@ -98,7 +99,7 @@ class BeatDetailPageUi extends React.PureComponent {
),
}));
- const columns = [
+ const columns: Array> = [
{
field: 'displayValue',
name: intl.formatMessage({
diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.examples.storyshot b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.examples.storyshot
index 7d37bf184b49b..b9c6d258821f2 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.examples.storyshot
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/dropdown_filter/component/__examples__/__snapshots__/dropdown_filter.examples.storyshot
@@ -16,9 +16,11 @@ exports[`Storyshots renderers/DropdownFilter default 1`] = `
"
+"
`;
exports[`Canvas Shareable Workpad API Placed successfully with height specified 1`] = `"
"`;
@@ -21,7 +21,7 @@ exports[`Canvas Shareable Workpad API Placed successfully with height specified
"
+"
`;
exports[`Canvas Shareable Workpad API Placed successfully with page specified 1`] = `"
"`;
@@ -33,7 +33,7 @@ exports[`Canvas Shareable Workpad API Placed successfully with page specified 2`
"
+"
`;
exports[`Canvas Shareable Workpad API Placed successfully with width and height specified 1`] = `"
"`;
@@ -45,7 +45,7 @@ exports[`Canvas Shareable Workpad API Placed successfully with width and height
"
+"
`;
exports[`Canvas Shareable Workpad API Placed successfully with width specified 1`] = `"
"`;
@@ -57,5 +57,5 @@ exports[`Canvas Shareable Workpad API Placed successfully with width specified 2
"
+"
`;
diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/components/__examples__/__snapshots__/canvas.examples.storyshot b/x-pack/legacy/plugins/canvas/shareable_runtime/components/__examples__/__snapshots__/canvas.examples.storyshot
index c3352b52c591d..6a33dba76c126 100644
--- a/x-pack/legacy/plugins/canvas/shareable_runtime/components/__examples__/__snapshots__/canvas.examples.storyshot
+++ b/x-pack/legacy/plugins/canvas/shareable_runtime/components/__examples__/__snapshots__/canvas.examples.storyshot
@@ -1325,9 +1325,11 @@ exports[`Storyshots shareables/Canvas component 1`] = `
title="Powered by Elastic.co"
>
App renders properly 1`] = `
"
+"
`;
diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/__examples__/__snapshots__/footer.examples.storyshot b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/__examples__/__snapshots__/footer.examples.storyshot
index 6570016336d9e..7b3ac299f80ad 100644
--- a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/__examples__/__snapshots__/footer.examples.storyshot
+++ b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/__examples__/__snapshots__/footer.examples.storyshot
@@ -1278,9 +1278,11 @@ exports[`Storyshots shareables/Footer contextual: austin 1`] = `
title="Powered by Elastic.co"
>
can navigate Autoplay Settings 1`] = `
class="euiContextMenu__itemLayout"
>
can navigate Autoplay Settings 1`] = `
Auto Play
@@ -95,13 +101,16 @@ exports[` can navigate Autoplay Settings 1`] = `
class="euiContextMenu__itemLayout"
>