>();
-const App = ({ navigation, data, history, kbnUrlStateStorage }: StateDemoAppDeps) => {
- const appStateContainer = useAppStateContainer();
- const appState = useAppState();
+export const App = ({
+ navigation,
+ data,
+ history,
+ kbnUrlStateStorage,
+ exampleLinks,
+ navigateToApp,
+}: StateDemoAppDeps) => {
+ const appStateContainer = useMemo(() => createStateContainer(defaultAppState), []);
+ const appState = useContainerState(appStateContainer);
useGlobalStateSyncing(data.query, kbnUrlStateStorage);
useAppStateSyncing(appStateContainer, data.query, kbnUrlStateStorage);
const indexPattern = useIndexPattern(data);
if (!indexPattern)
- return No index pattern found. Please create an index patter before loading...
;
+ return (
+
+ No index pattern found. Please create an index pattern before trying this example...
+
+ );
- // Render the application DOM.
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
return (
-
-
+
+
<>
-
-
-
-
-
-
-
-
-
-
-
- appStateContainer.set({ ...appState, name: e.target.value })}
- aria-label="My name"
- />
-
-
-
+
+
+
+ Integration with search bar
+
+
+
+
+ This examples shows how you can use state containers, state syncing utils and
+ helpers from data plugin to sync your app state and search bar state with the URL.
+
+
+
+
+
+
+
+ In addition to state from query bar also sync your arbitrary application state:
+
+
+ appStateContainer.set({ ...appState, name: e.target.value })}
+ aria-label="My name"
+ />
+
+
>
-
-
- );
-};
-
-export const StateDemoApp = (props: StateDemoAppDeps) => {
- const appStateContainer = useCreateStateContainer(defaultAppState);
-
- return (
-
-
-
+
+
);
};
-function useCreateStateContainer(
- defaultState: State
-): ReduxLikeStateContainer {
- const stateContainerRef = useRef | null>(null);
- if (!stateContainerRef.current) {
- stateContainerRef.current = createStateContainer(defaultState);
- }
- return stateContainerRef.current;
-}
-
function useIndexPattern(data: DataPublicPluginStart) {
const [indexPattern, setIndexPattern] = useState();
useEffect(() => {
diff --git a/examples/state_containers_examples/public/with_data_services/application.tsx b/examples/state_containers_examples/public/with_data_services/application.tsx
index d50c203a2a0797..4235446dd06e05 100644
--- a/examples/state_containers_examples/public/with_data_services/application.tsx
+++ b/examples/state_containers_examples/public/with_data_services/application.tsx
@@ -10,24 +10,26 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { AppMountParameters, CoreStart } from '../../../../src/core/public';
import { AppPluginDependencies } from './types';
-import { StateDemoApp } from './components/app';
+import { App } from './app';
import { createKbnUrlStateStorage } from '../../../../src/plugins/kibana_utils/public/';
+import { ExampleLink } from '../common/example_page';
export const renderApp = (
- { notifications, http }: CoreStart,
+ { notifications, application }: CoreStart,
{ navigation, data }: AppPluginDependencies,
- { element, history }: AppMountParameters
+ { element, history }: AppMountParameters,
+ { exampleLinks }: { exampleLinks: ExampleLink[] }
) => {
const kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: false, history });
ReactDOM.render(
- ,
element
);
diff --git a/examples/state_containers_examples/server/plugin.ts b/examples/state_containers_examples/server/plugin.ts
deleted file mode 100644
index 04ab4d7a0fede8..00000000000000
--- a/examples/state_containers_examples/server/plugin.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import {
- PluginInitializerContext,
- CoreSetup,
- CoreStart,
- Plugin,
- Logger,
-} from '../../../src/core/server';
-
-import { StateDemoPluginSetup, StateDemoPluginStart } from './types';
-import { defineRoutes } from './routes';
-
-export class StateDemoServerPlugin implements Plugin {
- private readonly logger: Logger;
-
- constructor(initializerContext: PluginInitializerContext) {
- this.logger = initializerContext.logger.get();
- }
-
- public setup(core: CoreSetup) {
- this.logger.debug('State_demo: Ssetup');
- const router = core.http.createRouter();
-
- // Register server side APIs
- defineRoutes(router);
-
- return {};
- }
-
- public start(core: CoreStart) {
- this.logger.debug('State_demo: Started');
- return {};
- }
-
- public stop() {}
-}
-
-export { StateDemoServerPlugin as Plugin };
diff --git a/examples/state_containers_examples/server/routes/index.ts b/examples/state_containers_examples/server/routes/index.ts
deleted file mode 100644
index f7c7a6abe88086..00000000000000
--- a/examples/state_containers_examples/server/routes/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { IRouter } from '../../../../src/core/server';
-
-export function defineRoutes(router: IRouter) {
- router.get(
- {
- path: '/api/state_demo/example',
- validate: false,
- },
- async (context, request, response) => {
- return response.ok({
- body: {
- time: new Date().toISOString(),
- },
- });
- }
- );
-}
diff --git a/package.json b/package.json
index ff6df054be220a..dac83dacf6fbfc 100644
--- a/package.json
+++ b/package.json
@@ -98,7 +98,7 @@
"@elastic/datemath": "link:packages/elastic-datemath",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary",
"@elastic/ems-client": "7.11.0",
- "@elastic/eui": "31.0.0",
+ "@elastic/eui": "31.3.0",
"@elastic/filesaver": "1.1.2",
"@elastic/good": "^9.0.1-kibana3",
"@elastic/node-crypto": "1.2.1",
@@ -287,7 +287,7 @@
"react-resizable": "^1.7.5",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
- "react-use": "^13.27.0",
+ "react-use": "^15.3.4",
"recompose": "^0.26.0",
"redux": "^4.0.5",
"redux-actions": "^2.6.5",
@@ -592,6 +592,7 @@
"base64-js": "^1.3.1",
"base64url": "^3.0.1",
"broadcast-channel": "^3.0.3",
+ "callsites": "^3.1.0",
"chai": "3.5.0",
"chance": "1.0.18",
"chromedriver": "^87.0.3",
@@ -827,7 +828,7 @@
"url-loader": "^2.2.0",
"use-resize-observer": "^6.0.0",
"val-loader": "^1.1.1",
- "vega": "^5.18.0",
+ "vega": "^5.19.1",
"vega-lite": "^4.17.0",
"vega-schema-url-parser": "^2.1.0",
"vega-tooltip": "^0.25.0",
diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap
index 4681057ad09980..f23d8c99369800 100644
--- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap
+++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap
@@ -108,4 +108,4 @@ exports[`prepares assets for distribution: baz bundle 1`] = `
exports[`prepares assets for distribution: foo async bundle 1`] = `"(window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[]).push([[1],{3:function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"foo\\",(function(){return foo}));function foo(){}}}]);"`;
-exports[`prepares assets for distribution: foo bundle 1`] = `"(function(modules){function webpackJsonpCallback(data){var chunkIds=data[0];var moreModules=data[1];var moduleId,chunkId,i=0,resolves=[];for(;i {
log.indent(-2);
});
diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts
index a138673d69ebf2..2a238cdeb53854 100644
--- a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts
+++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts
@@ -12,7 +12,32 @@ import { decorateSnapshotUi, expectSnapshot } from './decorate_snapshot_ui';
import path from 'path';
import fs from 'fs';
+const createMockTest = ({
+ title = 'Test',
+ passed = true,
+}: { title?: string; passed?: boolean } = {}) => {
+ return {
+ fullTitle: () => title,
+ isPassed: () => passed,
+ parent: {},
+ } as Test;
+};
+
describe('decorateSnapshotUi', () => {
+ const snapshotFolder = path.resolve(__dirname, '__snapshots__');
+ const snapshotFile = path.resolve(snapshotFolder, 'decorate_snapshot_ui.test.snap');
+
+ const cleanup = () => {
+ if (fs.existsSync(snapshotFile)) {
+ fs.unlinkSync(snapshotFile);
+ fs.rmdirSync(snapshotFolder);
+ }
+ };
+
+ beforeEach(cleanup);
+
+ afterAll(cleanup);
+
describe('when running a test', () => {
let lifecycle: Lifecycle;
beforeEach(() => {
@@ -21,15 +46,7 @@ describe('decorateSnapshotUi', () => {
});
it('passes when the snapshot matches the actual value', async () => {
- const test: Test = {
- title: 'Test',
- file: 'foo.ts',
- parent: {
- file: 'foo.ts',
- tests: [],
- suites: [],
- },
- } as any;
+ const test = createMockTest();
await lifecycle.beforeEachTest.trigger(test);
@@ -39,15 +56,7 @@ describe('decorateSnapshotUi', () => {
});
it('throws when the snapshot does not match the actual value', async () => {
- const test: Test = {
- title: 'Test',
- file: 'foo.ts',
- parent: {
- file: 'foo.ts',
- tests: [],
- suites: [],
- },
- } as any;
+ const test = createMockTest();
await lifecycle.beforeEachTest.trigger(test);
@@ -57,27 +66,10 @@ describe('decorateSnapshotUi', () => {
});
it('writes a snapshot to an external file if it does not exist', async () => {
- const test: Test = {
- title: 'Test',
- file: __filename,
- isPassed: () => true,
- } as any;
-
- // @ts-expect-error
- test.parent = {
- file: __filename,
- tests: [test],
- suites: [],
- };
+ const test: Test = createMockTest();
await lifecycle.beforeEachTest.trigger(test);
- const snapshotFile = path.resolve(
- __dirname,
- '__snapshots__',
- 'decorate_snapshot_ui.test.snap'
- );
-
expect(fs.existsSync(snapshotFile)).toBe(false);
expect(() => {
@@ -87,10 +79,48 @@ describe('decorateSnapshotUi', () => {
await lifecycle.afterTestSuite.trigger(test.parent);
expect(fs.existsSync(snapshotFile)).toBe(true);
+ });
+ });
- fs.unlinkSync(snapshotFile);
+ describe('when writing multiple snapshots to a single file', () => {
+ let lifecycle: Lifecycle;
+ beforeEach(() => {
+ lifecycle = new Lifecycle();
+ decorateSnapshotUi({ lifecycle, updateSnapshots: false, isCi: false });
+ });
+
+ beforeEach(() => {
+ fs.mkdirSync(path.resolve(__dirname, '__snapshots__'));
+ fs.writeFileSync(
+ snapshotFile,
+ `// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[\`Test1 1\`] = \`"foo"\`;
+
+exports[\`Test2 1\`] = \`"bar"\`;
+ `,
+ { encoding: 'utf-8' }
+ );
+ });
+
+ it('compares to an existing snapshot', async () => {
+ const test1 = createMockTest({ title: 'Test1' });
+
+ await lifecycle.beforeEachTest.trigger(test1);
+
+ expect(() => {
+ expectSnapshot('foo').toMatch();
+ }).not.toThrow();
+
+ const test2 = createMockTest({ title: 'Test2' });
- fs.rmdirSync(path.resolve(__dirname, '__snapshots__'));
+ await lifecycle.beforeEachTest.trigger(test2);
+
+ expect(() => {
+ expectSnapshot('foo').toMatch();
+ }).toThrow();
+
+ await lifecycle.afterTestSuite.trigger(test1.parent);
});
});
@@ -102,15 +132,7 @@ describe('decorateSnapshotUi', () => {
});
it("doesn't throw if the value does not match", async () => {
- const test: Test = {
- title: 'Test',
- file: 'foo.ts',
- parent: {
- file: 'foo.ts',
- tests: [],
- suites: [],
- },
- } as any;
+ const test = createMockTest();
await lifecycle.beforeEachTest.trigger(test);
@@ -128,15 +150,7 @@ describe('decorateSnapshotUi', () => {
});
it('throws on new snapshots', async () => {
- const test: Test = {
- title: 'Test',
- file: 'foo.ts',
- parent: {
- file: 'foo.ts',
- tests: [],
- suites: [],
- },
- } as any;
+ const test = createMockTest();
await lifecycle.beforeEachTest.trigger(test);
@@ -144,5 +158,82 @@ describe('decorateSnapshotUi', () => {
expectSnapshot('bar').toMatchInline();
}).toThrow();
});
+
+ describe('when adding to an existing file', () => {
+ beforeEach(() => {
+ fs.mkdirSync(path.resolve(__dirname, '__snapshots__'));
+ fs.writeFileSync(
+ snapshotFile,
+ `// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[\`Test 1\`] = \`"foo"\`;
+
+exports[\`Test2 1\`] = \`"bar"\`;
+ `,
+ { encoding: 'utf-8' }
+ );
+ });
+
+ it('does not throw on an existing test', async () => {
+ const test = createMockTest({ title: 'Test' });
+
+ await lifecycle.beforeEachTest.trigger(test);
+
+ expect(() => {
+ expectSnapshot('foo').toMatch();
+ }).not.toThrow();
+ });
+
+ it('throws on a new test', async () => {
+ const test = createMockTest({ title: 'New test' });
+
+ await lifecycle.beforeEachTest.trigger(test);
+
+ expect(() => {
+ expectSnapshot('foo').toMatch();
+ }).toThrow();
+ });
+
+ it('does not throw when all snapshots are used ', async () => {
+ const test = createMockTest({ title: 'Test' });
+
+ await lifecycle.beforeEachTest.trigger(test);
+
+ expect(() => {
+ expectSnapshot('foo').toMatch();
+ }).not.toThrow();
+
+ const test2 = createMockTest({ title: 'Test2' });
+
+ await lifecycle.beforeEachTest.trigger(test2);
+
+ expect(() => {
+ expectSnapshot('bar').toMatch();
+ }).not.toThrow();
+
+ const afterTestSuite = lifecycle.afterTestSuite.trigger({});
+
+ await expect(afterTestSuite).resolves.toBe(undefined);
+ });
+
+ it('throws on unused snapshots', async () => {
+ const test = createMockTest({ title: 'Test' });
+
+ await lifecycle.beforeEachTest.trigger(test);
+
+ expect(() => {
+ expectSnapshot('foo').toMatch();
+ }).not.toThrow();
+
+ const afterTestSuite = lifecycle.afterTestSuite.trigger({});
+
+ await expect(afterTestSuite).rejects.toMatchInlineSnapshot(`
+ [Error: 1 obsolete snapshot(s) found:
+ Test2 1.
+
+ Run tests again with \`--updateSnapshots\` to remove them.]
+ `);
+ });
+ });
});
});
diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts
index c43b50de3afd01..2111f1a6e5e900 100644
--- a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts
+++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts
@@ -15,9 +15,10 @@ import {
import path from 'path';
import prettier from 'prettier';
import babelTraverse from '@babel/traverse';
-import { flatten, once } from 'lodash';
+import { once } from 'lodash';
+import callsites from 'callsites';
import { Lifecycle } from '../lifecycle';
-import { Test, Suite } from '../../fake_mocha_types';
+import { Test } from '../../fake_mocha_types';
type ISnapshotState = InstanceType;
@@ -28,40 +29,17 @@ interface SnapshotContext {
currentTestName: string;
}
-let testContext: {
- file: string;
- snapshotTitle: string;
- snapshotContext: SnapshotContext;
-} | null = null;
-
-let registered: boolean = false;
-
-function getSnapshotMeta(currentTest: Test) {
- // Make sure snapshot title is unique per-file, rather than entire
- // suite. This allows reuse of tests, for instance to compare
- // results for different configurations.
-
- const titles = [currentTest.title];
- const file = currentTest.file;
-
- let test: Suite | undefined = currentTest?.parent;
-
- while (test && test.file === file) {
- titles.push(test.title);
- test = test.parent;
- }
-
- const snapshotTitle = titles.reverse().join(' ');
-
- if (!file || !snapshotTitle) {
- throw new Error(`file or snapshotTitle not available in Mocha test context`);
- }
-
- return {
- file,
- snapshotTitle,
- };
-}
+const globalState: {
+ updateSnapshot: SnapshotUpdateState;
+ registered: boolean;
+ currentTest: Test | null;
+ snapshots: Array<{ tests: Test[]; file: string; snapshotState: ISnapshotState }>;
+} = {
+ updateSnapshot: 'none',
+ registered: false,
+ currentTest: null,
+ snapshots: [],
+};
const modifyStackTracePrepareOnce = once(() => {
const originalPrepareStackTrace = Error.prepareStackTrace;
@@ -72,7 +50,7 @@ const modifyStackTracePrepareOnce = once(() => {
Error.prepareStackTrace = (error, structuredStackTrace) => {
let filteredStrackTrace: NodeJS.CallSite[] = structuredStackTrace;
- if (registered) {
+ if (globalState.registered) {
filteredStrackTrace = filteredStrackTrace.filter((callSite) => {
// check for both compiled and uncompiled files
return !callSite.getFileName()?.match(/decorate_snapshot_ui\.(js|ts)/);
@@ -94,21 +72,16 @@ export function decorateSnapshotUi({
updateSnapshots: boolean;
isCi: boolean;
}) {
- let snapshotStatesByFilePath: Record<
- string,
- { snapshotState: ISnapshotState; testsInFile: Test[] }
- > = {};
-
- registered = true;
-
- let updateSnapshot: SnapshotUpdateState;
+ globalState.registered = true;
+ globalState.snapshots.length = 0;
+ globalState.currentTest = null;
if (isCi) {
// make sure snapshots that have not been committed
// are not written to file on CI, passing the test
- updateSnapshot = 'none';
+ globalState.updateSnapshot = 'none';
} else {
- updateSnapshot = updateSnapshots ? 'all' : 'new';
+ globalState.updateSnapshot = updateSnapshots ? 'all' : 'new';
}
modifyStackTracePrepareOnce();
@@ -125,21 +98,8 @@ export function decorateSnapshotUi({
// @ts-expect-error
global.expectSnapshot = expectSnapshot;
- lifecycle.beforeEachTest.add((currentTest: Test) => {
- const { file, snapshotTitle } = getSnapshotMeta(currentTest);
-
- if (!snapshotStatesByFilePath[file]) {
- snapshotStatesByFilePath[file] = getSnapshotState(file, currentTest, updateSnapshot);
- }
-
- testContext = {
- file,
- snapshotTitle,
- snapshotContext: {
- snapshotState: snapshotStatesByFilePath[file].snapshotState,
- currentTestName: snapshotTitle,
- },
- };
+ lifecycle.beforeEachTest.add((test: Test) => {
+ globalState.currentTest = test;
});
lifecycle.afterTestSuite.add(function (testSuite) {
@@ -150,19 +110,18 @@ export function decorateSnapshotUi({
const unused: string[] = [];
- Object.keys(snapshotStatesByFilePath).forEach((file) => {
- const { snapshotState, testsInFile } = snapshotStatesByFilePath[file];
-
- testsInFile.forEach((test) => {
- const snapshotMeta = getSnapshotMeta(test);
+ globalState.snapshots.forEach((snapshot) => {
+ const { tests, snapshotState } = snapshot;
+ tests.forEach((test) => {
+ const title = test.fullTitle();
// If test is failed or skipped, mark snapshots as used. Otherwise,
// running a test in isolation will generate false positives.
if (!test.isPassed()) {
- snapshotState.markSnapshotsAsCheckedForTest(snapshotMeta.snapshotTitle);
+ snapshotState.markSnapshotsAsCheckedForTest(title);
}
});
- if (!updateSnapshots) {
+ if (globalState.updateSnapshot !== 'all') {
unused.push(...snapshotState.getUncheckedKeys());
} else {
snapshotState.removeUncheckedKeys();
@@ -179,28 +138,14 @@ export function decorateSnapshotUi({
);
}
- snapshotStatesByFilePath = {};
+ globalState.snapshots.length = 0;
});
}
-function recursivelyGetTestsFromSuite(suite: Suite): Test[] {
- return suite.tests.concat(flatten(suite.suites.map((s) => recursivelyGetTestsFromSuite(s))));
-}
-
-function getSnapshotState(file: string, test: Test, updateSnapshot: SnapshotUpdateState) {
+function getSnapshotState(file: string, updateSnapshot: SnapshotUpdateState) {
const dirname = path.dirname(file);
const filename = path.basename(file);
- let parent: Suite | undefined = test.parent;
-
- while (parent && parent.parent?.file === file) {
- parent = parent.parent;
- }
-
- if (!parent) {
- throw new Error('Top-level suite not found');
- }
-
const snapshotState = new SnapshotState(
path.join(dirname + `/__snapshots__/` + filename.replace(path.extname(filename), '.snap')),
{
@@ -211,24 +156,54 @@ function getSnapshotState(file: string, test: Test, updateSnapshot: SnapshotUpda
}
);
- return { snapshotState, testsInFile: recursivelyGetTestsFromSuite(parent) };
+ return snapshotState;
}
export function expectSnapshot(received: any) {
- if (!registered) {
+ if (!globalState.registered) {
throw new Error(
'Mocha hooks were not registered before expectSnapshot was used. Call `registerMochaHooksForSnapshots` in your top-level describe().'
);
}
- if (!testContext) {
- throw new Error('A current Mocha context is needed to match snapshots');
+ if (!globalState.currentTest) {
+ throw new Error('expectSnapshot can only be called inside of an it()');
+ }
+
+ const [, fileOfTest] = callsites().map((site) => site.getFileName());
+
+ if (!fileOfTest) {
+ throw new Error("Couldn't infer a filename for the current test");
+ }
+
+ let snapshot = globalState.snapshots.find(({ file }) => file === fileOfTest);
+
+ if (!snapshot) {
+ snapshot = {
+ file: fileOfTest,
+ tests: [],
+ snapshotState: getSnapshotState(fileOfTest, globalState.updateSnapshot),
+ };
+ globalState.snapshots.unshift(snapshot!);
+ }
+
+ if (!snapshot) {
+ throw new Error('Snapshot is undefined');
+ }
+
+ if (!snapshot.tests.includes(globalState.currentTest)) {
+ snapshot.tests.push(globalState.currentTest);
}
+ const context: SnapshotContext = {
+ snapshotState: snapshot.snapshotState,
+ currentTestName: globalState.currentTest.fullTitle(),
+ };
+
return {
- toMatch: expectToMatchSnapshot.bind(null, testContext.snapshotContext, received),
+ toMatch: expectToMatchSnapshot.bind(null, context, received),
// use bind to support optional 3rd argument (actual)
- toMatchInline: expectToMatchInlineSnapshot.bind(null, testContext.snapshotContext, received),
+ toMatchInline: expectToMatchInlineSnapshot.bind(null, context, received),
};
}
diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts
index 1a69c7db35a73c..b82254e5a14166 100644
--- a/src/core/public/doc_links/doc_links_service.ts
+++ b/src/core/public/doc_links/doc_links_service.ts
@@ -182,7 +182,12 @@ export class DocLinksService {
guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/maps.html`,
},
monitoring: {
+ alertsCluster: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/cluster-alerts.html`,
alertsKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html`,
+ alertsKibanaCpuThreshold: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-cpu-threshold`,
+ alertsKibanaDiskThreshold: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-disk-usage-threshold`,
+ alertsKibanaJvmThreshold: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-jvm-memory-threshold`,
+ alertsKibanaMissingData: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-missing-monitoring-data`,
monitorElasticsearch: `${ELASTICSEARCH_DOCS}configuring-metricbeat.html`,
monitorKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/monitoring-metricbeat.html`,
},
diff --git a/src/core/server/saved_objects/migrations/core/call_cluster.ts b/src/core/server/saved_objects/migrations/core/call_cluster.ts
index ba6027f711b62a..2cb1656833728b 100644
--- a/src/core/server/saved_objects/migrations/core/call_cluster.ts
+++ b/src/core/server/saved_objects/migrations/core/call_cluster.ts
@@ -18,7 +18,7 @@ export interface CallCluster {
(path: 'bulk', opts: { body: object[] }): Promise;
(path: 'count', opts: CountOpts): Promise<{ count: number; _shards: ShardsInfo }>;
(path: 'clearScroll', opts: { scrollId: string }): Promise;
- (path: 'indices.create' | 'indices.delete', opts: IndexCreationOpts): Promise;
+ (path: 'indices.create', opts: IndexCreationOpts): Promise;
(path: 'indices.exists', opts: IndexOpts): Promise;
(path: 'indices.existsAlias', opts: { name: string }): Promise;
(path: 'indices.get', opts: IndexOpts & Ignorable): Promise;
diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
index 741f715ba6ebe6..6ba652abda3d59 100644
--- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
+++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
@@ -206,6 +206,20 @@ describe('DocumentMigrator', () => {
);
});
+ it('coerces the current Kibana version if it has a hyphen', () => {
+ const validDefinition = {
+ kibanaVersion: '3.2.0-SNAPSHOT',
+ typeRegistry: createRegistry({
+ name: 'foo',
+ convertToMultiNamespaceTypeVersion: '3.2.0',
+ namespaceType: 'multiple',
+ }),
+ minimumConvertVersion: '0.0.0',
+ log: mockLogger,
+ };
+ expect(() => new DocumentMigrator(validDefinition)).not.toThrowError();
+ });
+
it('validates convertToMultiNamespaceTypeVersion is not used on a patch version', () => {
const invalidDefinition = {
kibanaVersion: '3.2.3',
diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts
index e4b89a949d3cf7..e93586ec7ce4c2 100644
--- a/src/core/server/saved_objects/migrations/core/document_migrator.ts
+++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts
@@ -159,10 +159,11 @@ export class DocumentMigrator implements VersionedTransformer {
*/
constructor({
typeRegistry,
- kibanaVersion,
+ kibanaVersion: rawKibanaVersion,
minimumConvertVersion = DEFAULT_MINIMUM_CONVERT_VERSION,
log,
}: DocumentMigratorOptions) {
+ const kibanaVersion = rawKibanaVersion.split('-')[0]; // coerce a semver-like string (x.y.z-SNAPSHOT) or prerelease version (x.y.z-alpha) to a regular semver (x.y.z)
validateMigrationDefinition(typeRegistry, kibanaVersion, minimumConvertVersion);
this.documentMigratorOptions = { typeRegistry, kibanaVersion, log };
diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts
index 32ecea94826ff9..4b35b017a7063e 100644
--- a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts
+++ b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts
@@ -107,17 +107,6 @@ describe('ElasticIndex', () => {
});
});
- describe('deleteIndex', () => {
- test('calls indices.delete', async () => {
- await Index.deleteIndex(client, '.lotr');
-
- expect(client.indices.delete).toHaveBeenCalledTimes(1);
- expect(client.indices.delete).toHaveBeenCalledWith({
- index: '.lotr',
- });
- });
- });
-
describe('claimAlias', () => {
test('handles unaliased indices', async () => {
client.indices.getAlias.mockResolvedValue(
diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts
index 9cdec926a56bae..aa7802320dfb72 100644
--- a/src/core/server/saved_objects/migrations/core/elastic_index.ts
+++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts
@@ -221,10 +221,6 @@ export async function createIndex(
});
}
-export async function deleteIndex(client: MigrationEsClient, index: string) {
- await client.indices.delete({ index });
-}
-
/**
* Converts an index to an alias. The `alias` parameter is the desired alias name which currently
* is a concrete index. This function will reindex `alias` into a new index, delete the `alias`
diff --git a/src/core/server/saved_objects/migrations/core/migration_es_client.ts b/src/core/server/saved_objects/migrations/core/migration_es_client.ts
index 2653e96e1b8d5c..c4bb19d8223fff 100644
--- a/src/core/server/saved_objects/migrations/core/migration_es_client.ts
+++ b/src/core/server/saved_objects/migrations/core/migration_es_client.ts
@@ -20,7 +20,6 @@ const methods = [
'clearScroll',
'count',
'indices.create',
- 'indices.delete',
'indices.deleteTemplate',
'indices.get',
'indices.getAlias',
diff --git a/src/core/server/ui_settings/integration_tests/routes.test.ts b/src/core/server/ui_settings/integration_tests/routes.test.ts
index 89f5b6732a8fb2..2f0a5ebc139e67 100644
--- a/src/core/server/ui_settings/integration_tests/routes.test.ts
+++ b/src/core/server/ui_settings/integration_tests/routes.test.ts
@@ -9,7 +9,8 @@
import { schema } from '@kbn/config-schema';
import * as kbnTestServer from '../../../test_helpers/kbn_server';
-describe('ui settings service', () => {
+// FLAKY: https://github.com/elastic/kibana/issues/89191
+describe.skip('ui settings service', () => {
describe('routes', () => {
let root: ReturnType;
beforeAll(async () => {
diff --git a/src/core/server/ui_settings/settings/navigation.test.ts b/src/core/server/ui_settings/settings/navigation.test.ts
index 1ba81b4e79f46b..0e6fbdfdbdb6e2 100644
--- a/src/core/server/ui_settings/settings/navigation.test.ts
+++ b/src/core/server/ui_settings/settings/navigation.test.ts
@@ -28,18 +28,4 @@ describe('navigation settings', () => {
);
});
});
-
- describe('pageNavigation', () => {
- const validate = getValidationFn(navigationSettings.pageNavigation);
-
- it('should only accept valid values', () => {
- expect(() => validate('modern')).not.toThrow();
- expect(() => validate('legacy')).not.toThrow();
- expect(() => validate('invalid')).toThrowErrorMatchingInlineSnapshot(`
-"types that failed validation:
-- [0]: expected value to equal [modern]
-- [1]: expected value to equal [legacy]"
-`);
- });
- });
});
diff --git a/src/core/server/ui_settings/settings/navigation.ts b/src/core/server/ui_settings/settings/navigation.ts
index 38064db9e9388d..937af4bb9aad1c 100644
--- a/src/core/server/ui_settings/settings/navigation.ts
+++ b/src/core/server/ui_settings/settings/navigation.ts
@@ -37,25 +37,5 @@ export const getNavigationSettings = (): Record => {
'The route must be a relative URL.',
}),
},
- pageNavigation: {
- name: i18n.translate('core.ui_settings.params.pageNavigationName', {
- defaultMessage: 'Side nav style',
- }),
- value: 'modern',
- description: i18n.translate('core.ui_settings.params.pageNavigationDesc', {
- defaultMessage: 'Change the style of navigation',
- }),
- type: 'select',
- options: ['modern', 'legacy'],
- optionLabels: {
- modern: i18n.translate('core.ui_settings.params.pageNavigationModern', {
- defaultMessage: 'Modern',
- }),
- legacy: i18n.translate('core.ui_settings.params.pageNavigationLegacy', {
- defaultMessage: 'Legacy',
- }),
- },
- schema: schema.oneOf([schema.literal('modern'), schema.literal('legacy')]),
- },
};
};
diff --git a/src/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts
index 20865bea2f897a..7c28db333cc830 100644
--- a/src/plugins/charts/public/static/utils/transform_click_event.ts
+++ b/src/plugins/charts/public/static/utils/transform_click_event.ts
@@ -30,6 +30,9 @@ export interface BrushTriggerEvent {
type AllSeriesAccessors = Array<[accessor: Accessor | AccessorFn, value: string | number]>;
+// TODO: replace when exported from elastic/charts
+const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__';
+
/**
* returns accessor value from string or function accessor
* @param datum
@@ -82,6 +85,29 @@ const getAllSplitAccessors = (
value,
]);
+/**
+ * Gets value from small multiple accessors
+ *
+ * Only handles single small multiple accessor
+ */
+function getSplitChartValue({
+ smHorizontalAccessorValue,
+ smVerticalAccessorValue,
+}: Pick):
+ | string
+ | number
+ | undefined {
+ if (smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) {
+ return smHorizontalAccessorValue;
+ }
+
+ if (smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) {
+ return smVerticalAccessorValue;
+ }
+
+ return;
+}
+
/**
* Reduces matching column indexes
*
@@ -92,7 +118,8 @@ const getAllSplitAccessors = (
const columnReducer = (
xAccessor: Accessor | AccessorFn | null,
yAccessor: Accessor | AccessorFn | null,
- splitAccessors: AllSeriesAccessors
+ splitAccessors: AllSeriesAccessors,
+ splitChartAccessor?: Accessor | AccessorFn
) => (
acc: Array<[index: number, id: string]>,
{ id }: Datatable['columns'][number],
@@ -101,6 +128,7 @@ const columnReducer = (
if (
(xAccessor !== null && validateAccessorId(id, xAccessor)) ||
(yAccessor !== null && validateAccessorId(id, yAccessor)) ||
+ (splitChartAccessor !== undefined && validateAccessorId(id, splitChartAccessor)) ||
splitAccessors.some(([accessor]) => validateAccessorId(id, accessor))
) {
acc.push([index, id]);
@@ -121,13 +149,18 @@ const rowFindPredicate = (
geometry: GeometryValue | null,
xAccessor: Accessor | AccessorFn | null,
yAccessor: Accessor | AccessorFn | null,
- splitAccessors: AllSeriesAccessors
+ splitAccessors: AllSeriesAccessors,
+ splitChartAccessor?: Accessor | AccessorFn,
+ splitChartValue?: string | number
) => (row: Datatable['rows'][number]): boolean =>
(geometry === null ||
(xAccessor !== null &&
getAccessorValue(row, xAccessor) === geometry.x &&
yAccessor !== null &&
- getAccessorValue(row, yAccessor) === geometry.y)) &&
+ getAccessorValue(row, yAccessor) === geometry.y &&
+ (splitChartAccessor === undefined ||
+ (splitChartValue !== undefined &&
+ getAccessorValue(row, splitChartAccessor) === splitChartValue)))) &&
[...splitAccessors].every(([accessor, value]) => getAccessorValue(row, accessor) === value);
/**
@@ -142,19 +175,28 @@ export const getFilterFromChartClickEventFn = (
table: Datatable,
xAccessor: Accessor | AccessorFn,
splitSeriesAccessorFnMap?: Map,
+ splitChartAccessor?: Accessor | AccessorFn,
negate: boolean = false
) => (points: Array<[GeometryValue, XYChartSeriesIdentifier]>): ClickTriggerEvent => {
const data: ValueClickContext['data']['data'] = [];
points.forEach((point) => {
const [geometry, { yAccessor, splitAccessors }] = point;
+ const splitChartValue = getSplitChartValue(point[1]);
const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap);
const columns = table.columns.reduce>(
- columnReducer(xAccessor, yAccessor, allSplitAccessors),
+ columnReducer(xAccessor, yAccessor, allSplitAccessors, splitChartAccessor),
[]
);
const row = table.rows.findIndex(
- rowFindPredicate(geometry, xAccessor, yAccessor, allSplitAccessors)
+ rowFindPredicate(
+ geometry,
+ xAccessor,
+ yAccessor,
+ allSplitAccessors,
+ splitChartAccessor,
+ splitChartValue
+ )
);
const newData = columns.map(([column, id]) => ({
table,
@@ -179,16 +221,20 @@ export const getFilterFromChartClickEventFn = (
* Helper function to get filter action event from series
*/
export const getFilterFromSeriesFn = (table: Datatable) => (
- { splitAccessors }: XYChartSeriesIdentifier,
+ { splitAccessors, ...rest }: XYChartSeriesIdentifier,
splitSeriesAccessorFnMap?: Map,
+ splitChartAccessor?: Accessor | AccessorFn,
negate = false
): ClickTriggerEvent => {
+ const splitChartValue = getSplitChartValue(rest);
const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap);
const columns = table.columns.reduce>(
- columnReducer(null, null, allSplitAccessors),
+ columnReducer(null, null, allSplitAccessors, splitChartAccessor),
[]
);
- const row = table.rows.findIndex(rowFindPredicate(null, null, null, allSplitAccessors));
+ const row = table.rows.findIndex(
+ rowFindPredicate(null, null, null, allSplitAccessors, splitChartAccessor, splitChartValue)
+ );
const data: ValueClickContext['data']['data'] = columns.map(([column, id]) => ({
table,
column,
diff --git a/src/plugins/dashboard/public/application/lib/session_restoration.test.ts b/src/plugins/dashboard/public/application/lib/session_restoration.test.ts
new file mode 100644
index 00000000000000..56db5346b7c6c9
--- /dev/null
+++ b/src/plugins/dashboard/public/application/lib/session_restoration.test.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+import { dataPluginMock } from '../../../../data/public/mocks';
+import { createSessionRestorationDataProvider } from './session_restoration';
+import { getAppStateDefaults } from './get_app_state_defaults';
+import { getSavedDashboardMock } from '../test_helpers';
+import { SavedObjectTagDecoratorTypeGuard } from '../../../../saved_objects_tagging_oss/public';
+
+describe('createSessionRestorationDataProvider', () => {
+ const mockDataPlugin = dataPluginMock.createStartContract();
+ const searchSessionInfoProvider = createSessionRestorationDataProvider({
+ data: mockDataPlugin,
+ getAppState: () =>
+ getAppStateDefaults(
+ getSavedDashboardMock(),
+ false,
+ ((() => false) as unknown) as SavedObjectTagDecoratorTypeGuard
+ ),
+ getDashboardTitle: () => 'Dashboard',
+ getDashboardId: () => 'Id',
+ });
+
+ describe('session state', () => {
+ test('restoreState has sessionId and initialState has not', async () => {
+ const searchSessionId = 'id';
+ (mockDataPlugin.search.session.getSessionId as jest.Mock).mockImplementation(
+ () => searchSessionId
+ );
+ const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData();
+ expect(initialState.searchSessionId).toBeUndefined();
+ expect(restoreState.searchSessionId).toBe(searchSessionId);
+ });
+
+ test('restoreState has absoluteTimeRange', async () => {
+ const relativeTime = 'relativeTime';
+ const absoluteTime = 'absoluteTime';
+ (mockDataPlugin.query.timefilter.timefilter.getTime as jest.Mock).mockImplementation(
+ () => relativeTime
+ );
+ (mockDataPlugin.query.timefilter.timefilter.getAbsoluteTime as jest.Mock).mockImplementation(
+ () => absoluteTime
+ );
+ const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData();
+ expect(initialState.timeRange).toBe(relativeTime);
+ expect(restoreState.timeRange).toBe(absoluteTime);
+ });
+
+ test('restoreState has refreshInterval paused', async () => {
+ const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData();
+ expect(initialState.refreshInterval).toBeUndefined();
+ expect(restoreState.refreshInterval?.pause).toBe(true);
+ });
+ });
+});
diff --git a/src/plugins/dashboard/public/application/lib/session_restoration.ts b/src/plugins/dashboard/public/application/lib/session_restoration.ts
index 60a0c56a63218a..fb57f8caa5ce4e 100644
--- a/src/plugins/dashboard/public/application/lib/session_restoration.ts
+++ b/src/plugins/dashboard/public/application/lib/session_restoration.ts
@@ -21,8 +21,8 @@ export function createSessionRestorationDataProvider(deps: {
getUrlGeneratorData: async () => {
return {
urlGeneratorId: DASHBOARD_APP_URL_GENERATOR,
- initialState: getUrlGeneratorState({ ...deps, forceAbsoluteTime: false }),
- restoreState: getUrlGeneratorState({ ...deps, forceAbsoluteTime: true }),
+ initialState: getUrlGeneratorState({ ...deps, shouldRestoreSearchSession: false }),
+ restoreState: getUrlGeneratorState({ ...deps, shouldRestoreSearchSession: true }),
};
},
};
@@ -32,20 +32,17 @@ function getUrlGeneratorState({
data,
getAppState,
getDashboardId,
- forceAbsoluteTime,
+ shouldRestoreSearchSession,
}: {
data: DataPublicPluginStart;
getAppState: () => DashboardAppState;
getDashboardId: () => string;
- /**
- * Can force time range from time filter to convert from relative to absolute time range
- */
- forceAbsoluteTime: boolean;
+ shouldRestoreSearchSession: boolean;
}): DashboardUrlGeneratorState {
const appState = getAppState();
return {
dashboardId: getDashboardId(),
- timeRange: forceAbsoluteTime
+ timeRange: shouldRestoreSearchSession
? data.query.timefilter.timefilter.getAbsoluteTime()
: data.query.timefilter.timefilter.getTime(),
filters: data.query.filterManager.getFilters(),
@@ -55,6 +52,12 @@ function getUrlGeneratorState({
preserveSavedFilters: false,
viewMode: appState.viewMode,
panels: getDashboardId() ? undefined : appState.panels,
- searchSessionId: data.search.session.getSessionId(),
+ searchSessionId: shouldRestoreSearchSession ? data.search.session.getSessionId() : undefined,
+ refreshInterval: shouldRestoreSearchSession
+ ? {
+ pause: true, // force pause refresh interval when restoring a session
+ value: 0,
+ }
+ : undefined,
};
}
diff --git a/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap b/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap
index 3e09fa449a1aa3..4ef61ec0f25571 100644
--- a/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap
+++ b/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap
@@ -57,9 +57,16 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": undefined,
"lang": "lang",
"name": "name",
"readFromDocValues": false,
+ "runtimeField": Object {
+ "script": Object {
+ "source": "emit('hello world')",
+ },
+ "type": "keyword",
+ },
"script": "script",
"scripted": true,
"searchable": true,
diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts
index bce75f9932479a..8a73abb3c7d830 100644
--- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts
+++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts
@@ -9,7 +9,7 @@
import { IndexPatternField } from './index_pattern_field';
import { IndexPattern } from '../index_patterns';
import { KBN_FIELD_TYPES, FieldFormat } from '../../../common';
-import { FieldSpec } from '../types';
+import { FieldSpec, RuntimeField } from '../types';
describe('Field', function () {
function flatten(obj: Record) {
@@ -42,6 +42,12 @@ describe('Field', function () {
} as unknown) as IndexPattern,
$$spec: ({} as unknown) as FieldSpec,
conflictDescriptions: { a: ['b', 'c'], d: ['e'] },
+ runtimeField: {
+ type: 'keyword' as RuntimeField['type'],
+ script: {
+ source: "emit('hello world')",
+ },
+ },
};
it('the correct properties are writable', () => {
diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
index 540563c3a8cfc7..ed6c4bd40d5616 100644
--- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
+++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
@@ -6,9 +6,10 @@
* Public License, v 1.
*/
+import type { RuntimeField } from '../types';
import { KbnFieldType, getKbnFieldType } from '../../kbn_field_types';
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
-import { IFieldType } from './types';
+import type { IFieldType } from './types';
import { FieldSpec, IndexPattern } from '../..';
import { shortenDottedString } from '../../utils';
@@ -35,6 +36,14 @@ export class IndexPatternField implements IFieldType {
this.spec.count = count;
}
+ public get runtimeField() {
+ return this.spec.runtimeField;
+ }
+
+ public set runtimeField(runtimeField: RuntimeField | undefined) {
+ this.spec.runtimeField = runtimeField;
+ }
+
/**
* Script field code
*/
@@ -117,6 +126,13 @@ export class IndexPatternField implements IFieldType {
return this.spec.subType;
}
+ /**
+ * Is the field part of the index mapping?
+ */
+ public get isMapped() {
+ return this.spec.isMapped;
+ }
+
// not writable, not serialized
public get sortable() {
return (
@@ -181,6 +197,8 @@ export class IndexPatternField implements IFieldType {
format: getFormatterForField ? getFormatterForField(this).toJSON() : undefined,
customLabel: this.customLabel,
shortDotsEnable: this.spec.shortDotsEnable,
+ runtimeField: this.runtimeField,
+ isMapped: this.isMapped,
};
}
}
diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap
index 76de2b2662bb02..4aadddfad3b970 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap
+++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap
@@ -20,9 +20,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "@tags",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -44,9 +46,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "@timestamp",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -68,9 +72,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "_id",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -92,9 +98,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "_source",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -116,9 +124,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "_type",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -140,9 +150,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "area",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -164,9 +176,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "bytes",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -188,9 +202,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "custom_user_field",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -212,9 +228,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "extension",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -236,9 +254,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "extension.keyword",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -264,9 +284,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "geo.coordinates",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -288,9 +310,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "geo.src",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -312,9 +336,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "hashed",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -336,9 +362,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "ip",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -360,9 +388,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "machine.os",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -384,9 +414,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "machine.os.raw",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -412,9 +444,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "non-filterable",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": false,
@@ -436,9 +470,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "non-sortable",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": false,
@@ -460,9 +496,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "phpmemory",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -484,9 +522,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "point",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -508,9 +548,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "request_body",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -518,6 +560,35 @@ Object {
"subType": undefined,
"type": "attachment",
},
+ "runtime_field": Object {
+ "aggregatable": false,
+ "conflictDescriptions": undefined,
+ "count": 0,
+ "customLabel": undefined,
+ "esTypes": undefined,
+ "format": Object {
+ "id": "number",
+ "params": Object {
+ "pattern": "$0,0.[00]",
+ },
+ },
+ "isMapped": undefined,
+ "lang": undefined,
+ "name": "runtime_field",
+ "readFromDocValues": false,
+ "runtimeField": Object {
+ "script": Object {
+ "source": "emit('hello world')",
+ },
+ "type": "keyword",
+ },
+ "script": undefined,
+ "scripted": false,
+ "searchable": false,
+ "shortDotsEnable": false,
+ "subType": undefined,
+ "type": undefined,
+ },
"script date": Object {
"aggregatable": true,
"conflictDescriptions": undefined,
@@ -532,9 +603,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": false,
"lang": "painless",
"name": "script date",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": "1234",
"scripted": true,
"searchable": true,
@@ -556,9 +629,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": false,
"lang": "expression",
"name": "script murmur3",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": "1234",
"scripted": true,
"searchable": true,
@@ -580,9 +655,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": false,
"lang": "expression",
"name": "script number",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": "1234",
"scripted": true,
"searchable": true,
@@ -604,9 +681,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": false,
"lang": "expression",
"name": "script string",
"readFromDocValues": false,
+ "runtimeField": undefined,
"script": "'i am a string'",
"scripted": true,
"searchable": true,
@@ -628,9 +707,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "ssl",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -652,9 +733,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "time",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -676,9 +759,11 @@ Object {
"pattern": "$0,0.[00]",
},
},
+ "isMapped": true,
"lang": undefined,
"name": "utc_time",
"readFromDocValues": true,
+ "runtimeField": undefined,
"script": undefined,
"scripted": false,
"searchable": true,
@@ -689,6 +774,14 @@ Object {
},
"id": "test-pattern",
"intervalName": undefined,
+ "runtimeFieldMap": Object {
+ "runtime_field": Object {
+ "script": Object {
+ "source": "emit('hello world')",
+ },
+ "type": "keyword",
+ },
+ },
"sourceFilters": undefined,
"timeFieldName": "timestamp",
"title": "title",
diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap
index bad74430b89668..d6da4adac81a4c 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap
+++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap
@@ -10,6 +10,7 @@ Object {
"fields": Object {},
"id": "id",
"intervalName": undefined,
+ "runtimeFieldMap": Object {},
"sourceFilters": Array [
Object {
"value": "item1",
diff --git a/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js b/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js
index 3e81b9234ee644..2bcb8df34cf026 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js
+++ b/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js
@@ -68,6 +68,7 @@ function stubbedLogstashFields() {
lang,
scripted,
subType,
+ isMapped: !scripted,
};
});
}
diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts
index bb7ed17f9e6086..4f6e83460aecf2 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts
@@ -18,9 +18,27 @@ import { IndexPatternField } from '../fields';
import { fieldFormatsMock } from '../../field_formats/mocks';
import { FieldFormat } from '../..';
+import { RuntimeField } from '../types';
class MockFieldFormatter {}
+const runtimeFieldScript = {
+ type: 'keyword' as RuntimeField['type'],
+ script: {
+ source: "emit('hello world')",
+ },
+};
+
+const runtimeFieldMap = {
+ runtime_field: runtimeFieldScript,
+};
+
+const runtimeField = {
+ name: 'runtime_field',
+ runtimeField: runtimeFieldScript,
+ scripted: false,
+};
+
fieldFormatsMock.getInstance = jest.fn().mockImplementation(() => new MockFieldFormatter()) as any;
// helper function to create index patterns
@@ -32,7 +50,15 @@ function create(id: string) {
} = stubbedSavedObjectIndexPattern(id);
return new IndexPattern({
- spec: { id, type, version, timeFieldName, fields, title },
+ spec: {
+ id,
+ type,
+ version,
+ timeFieldName,
+ fields: { ...fields, runtime_field: runtimeField },
+ title,
+ runtimeFieldMap,
+ },
fieldFormats: fieldFormatsMock,
shortDotsEnable: false,
metaFields: [],
@@ -53,6 +79,10 @@ describe('IndexPattern', () => {
expect(indexPattern).toHaveProperty('getNonScriptedFields');
expect(indexPattern).toHaveProperty('addScriptedField');
expect(indexPattern).toHaveProperty('removeScriptedField');
+ expect(indexPattern).toHaveProperty('addScriptedField');
+ expect(indexPattern).toHaveProperty('removeScriptedField');
+ expect(indexPattern).toHaveProperty('addRuntimeField');
+ expect(indexPattern).toHaveProperty('removeRuntimeField');
// properties
expect(indexPattern).toHaveProperty('fields');
@@ -65,6 +95,7 @@ describe('IndexPattern', () => {
expect(indexPattern.fields[0]).toHaveProperty('filterable');
expect(indexPattern.fields[0]).toHaveProperty('sortable');
expect(indexPattern.fields[0]).toHaveProperty('scripted');
+ expect(indexPattern.fields[0]).toHaveProperty('isMapped');
});
});
@@ -98,6 +129,12 @@ describe('IndexPattern', () => {
expect(docValueFieldNames).toContain('utc_time');
});
+ test('should return runtimeField', () => {
+ expect(indexPattern.getComputedFields().runtimeFields).toEqual({
+ runtime_field: runtimeFieldScript,
+ });
+ });
+
test('should request date field doc values in date_time format', () => {
const { docvalueFields } = indexPattern.getComputedFields();
const timestampField = docvalueFields.find((field) => field.field === '@timestamp');
@@ -117,6 +154,7 @@ describe('IndexPattern', () => {
const notScriptedNames = mockLogStashFields()
.filter((item: IndexPatternField) => item.scripted === false)
.map((item: IndexPatternField) => item.name);
+ notScriptedNames.push('runtime_field');
const respNames = map(indexPattern.getNonScriptedFields(), 'name');
expect(respNames).toEqual(notScriptedNames);
@@ -185,6 +223,52 @@ describe('IndexPattern', () => {
});
});
+ describe('addRuntimeField and removeRuntimeField', () => {
+ const runtime = {
+ type: 'keyword' as RuntimeField['type'],
+ script: {
+ source: "emit('hello world');",
+ },
+ };
+
+ beforeEach(() => {
+ const formatter = {
+ toJSON: () => ({ id: 'bytes' }),
+ } as FieldFormat;
+ indexPattern.getFormatterForField = () => formatter;
+ });
+
+ test('add and remove runtime field to existing field', () => {
+ indexPattern.addRuntimeField('@tags', runtime);
+ expect(indexPattern.toSpec().runtimeFieldMap).toEqual({
+ '@tags': runtime,
+ runtime_field: runtimeField.runtimeField,
+ });
+ expect(indexPattern.toSpec()!.fields!['@tags'].runtimeField).toEqual(runtime);
+
+ indexPattern.removeRuntimeField('@tags');
+ expect(indexPattern.toSpec().runtimeFieldMap).toEqual({
+ runtime_field: runtimeField.runtimeField,
+ });
+ expect(indexPattern.toSpec()!.fields!['@tags'].runtimeField).toBeUndefined();
+ });
+
+ test('add and remove runtime field as new field', () => {
+ indexPattern.addRuntimeField('new_field', runtime);
+ expect(indexPattern.toSpec().runtimeFieldMap).toEqual({
+ runtime_field: runtimeField.runtimeField,
+ new_field: runtime,
+ });
+ expect(indexPattern.toSpec()!.fields!.new_field.runtimeField).toEqual(runtime);
+
+ indexPattern.removeRuntimeField('new_field');
+ expect(indexPattern.toSpec().runtimeFieldMap).toEqual({
+ runtime_field: runtimeField.runtimeField,
+ });
+ expect(indexPattern.toSpec()!.fields!.new_field).toBeUndefined();
+ });
+ });
+
describe('getFormatterForField', () => {
test('should return the default one for empty objects', () => {
indexPattern.setFieldFormat('scriptedFieldWithEmptyFormatter', {});
diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
index 452c663d96716c..144d38fe15909f 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
@@ -8,6 +8,7 @@
import _, { each, reject } from 'lodash';
import { FieldAttrs, FieldAttrSet } from '../..';
+import type { RuntimeField } from '../types';
import { DuplicateField } from '../../../../kibana_utils/common';
import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common';
@@ -17,6 +18,7 @@ import { flattenHitWrapper } from './flatten_hit';
import { FieldFormatsStartCommon, FieldFormat } from '../../field_formats';
import { IndexPatternSpec, TypeMeta, SourceFilter, IndexPatternFieldMap } from '../types';
import { SerializedFieldFormat } from '../../../../expressions/common';
+import { castEsToKbnFieldTypeName } from '../../kbn_field_types';
interface IndexPatternDeps {
spec?: IndexPatternSpec;
@@ -74,6 +76,8 @@ export class IndexPattern implements IIndexPattern {
private shortDotsEnable: boolean = false;
private fieldFormats: FieldFormatsStartCommon;
private fieldAttrs: FieldAttrs;
+ private runtimeFieldMap: Record;
+
/**
* prevents errors when index pattern exists before indices
*/
@@ -115,6 +119,7 @@ export class IndexPattern implements IIndexPattern {
this.fieldAttrs = spec.fieldAttrs || {};
this.intervalName = spec.intervalName;
this.allowNoIndex = spec.allowNoIndex || false;
+ this.runtimeFieldMap = spec.runtimeFieldMap || {};
}
/**
@@ -160,7 +165,8 @@ export class IndexPattern implements IIndexPattern {
return {
storedFields: ['*'],
scriptFields,
- docvalueFields: [],
+ docvalueFields: [] as Array<{ field: string; format: string }>,
+ runtimeFields: {},
};
}
@@ -192,6 +198,7 @@ export class IndexPattern implements IIndexPattern {
storedFields: ['*'],
scriptFields,
docvalueFields,
+ runtimeFields: this.runtimeFieldMap,
};
}
@@ -210,6 +217,7 @@ export class IndexPattern implements IIndexPattern {
typeMeta: this.typeMeta,
type: this.type,
fieldFormats: this.fieldFormatMap,
+ runtimeFieldMap: this.runtimeFieldMap,
fieldAttrs: this.fieldAttrs,
intervalName: this.intervalName,
allowNoIndex: this.allowNoIndex,
@@ -305,6 +313,7 @@ export class IndexPattern implements IIndexPattern {
? undefined
: JSON.stringify(this.fieldFormatMap);
const fieldAttrs = this.getFieldAttrs();
+ const runtimeFieldMap = this.runtimeFieldMap;
return {
fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined,
@@ -319,6 +328,7 @@ export class IndexPattern implements IIndexPattern {
type: this.type,
typeMeta: this.typeMeta ? JSON.stringify(this.typeMeta) : undefined,
allowNoIndex: this.allowNoIndex ? this.allowNoIndex : undefined,
+ runtimeFieldMap: runtimeFieldMap ? JSON.stringify(runtimeFieldMap) : undefined,
};
}
@@ -340,6 +350,51 @@ export class IndexPattern implements IIndexPattern {
);
}
+ /**
+ * Add a runtime field - Appended to existing mapped field or a new field is
+ * created as appropriate
+ * @param name Field name
+ * @param runtimeField Runtime field definition
+ */
+
+ addRuntimeField(name: string, runtimeField: RuntimeField) {
+ const existingField = this.getFieldByName(name);
+ if (existingField) {
+ existingField.runtimeField = runtimeField;
+ } else {
+ this.fields.add({
+ name,
+ runtimeField,
+ type: castEsToKbnFieldTypeName(runtimeField.type),
+ aggregatable: true,
+ searchable: true,
+ count: 0,
+ readFromDocValues: false,
+ });
+ }
+ this.runtimeFieldMap[name] = runtimeField;
+ }
+
+ /**
+ * Remove a runtime field - removed from mapped field or removed unmapped
+ * field as appropriate
+ * @param name Field name
+ */
+
+ removeRuntimeField(name: string) {
+ const existingField = this.getFieldByName(name);
+ if (existingField) {
+ if (existingField.isMapped) {
+ // mapped field, remove runtimeField def
+ existingField.runtimeField = undefined;
+ } else {
+ // runtimeField only
+ this.fields.remove(existingField);
+ }
+ }
+ delete this.runtimeFieldMap[name];
+ }
+
/**
* Get formatter for a given field name. Return undefined if none exists
* @param field
diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
index 80cb8a55fa0a02..60436da530b636 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
@@ -11,6 +11,7 @@ import { PublicMethodsOf } from '@kbn/utility-types';
import { SavedObjectsClientCommon } from '../..';
import { createIndexPatternCache } from '.';
+import type { RuntimeField } from '../types';
import { IndexPattern } from './index_pattern';
import {
createEnsureDefaultIndexPattern,
@@ -34,6 +35,7 @@ import { SavedObjectNotFound } from '../../../../kibana_utils/common';
import { IndexPatternMissingIndices } from '../lib';
import { findByTitle } from '../utils';
import { DuplicateIndexPatternError } from '../errors';
+import { castEsToKbnFieldTypeName } from '../../kbn_field_types';
const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3;
const savedObjectType = 'index-pattern';
@@ -247,7 +249,8 @@ export class IndexPatternsService {
*/
refreshFields = async (indexPattern: IndexPattern) => {
try {
- const fields = await this.getFieldsForIndexPattern(indexPattern);
+ const fields = (await this.getFieldsForIndexPattern(indexPattern)) as FieldSpec[];
+ fields.forEach((field) => (field.isMapped = true));
const scripted = indexPattern.getScriptedFields().map((field) => field.spec);
const fieldAttrs = indexPattern.getFieldAttrs();
const fieldsWithSavedAttrs = Object.values(
@@ -288,6 +291,7 @@ export class IndexPatternsService {
try {
let updatedFieldList: FieldSpec[];
const newFields = (await this.getFieldsForWildcard(options)) as FieldSpec[];
+ newFields.forEach((field) => (field.isMapped = true));
// If allowNoIndex, only update field list if field caps finds fields. To support
// beats creating index pattern and dashboard before docs
@@ -347,6 +351,7 @@ export class IndexPatternsService {
fields,
sourceFilters,
fieldFormatMap,
+ runtimeFieldMap,
typeMeta,
type,
fieldAttrs,
@@ -359,6 +364,9 @@ export class IndexPatternsService {
const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {};
const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : [];
const parsedFieldAttrs: FieldAttrs = fieldAttrs ? JSON.parse(fieldAttrs) : {};
+ const parsedRuntimeFieldMap: Record = runtimeFieldMap
+ ? JSON.parse(runtimeFieldMap)
+ : {};
return {
id,
@@ -373,6 +381,7 @@ export class IndexPatternsService {
fieldFormats: parsedFieldFormatMap,
fieldAttrs: parsedFieldAttrs,
allowNoIndex,
+ runtimeFieldMap: parsedRuntimeFieldMap,
};
};
@@ -387,7 +396,7 @@ export class IndexPatternsService {
}
const spec = this.savedObjectToSpec(savedObject);
- const { title, type, typeMeta } = spec;
+ const { title, type, typeMeta, runtimeFieldMap } = spec;
spec.fieldAttrs = savedObject.attributes.fieldAttrs
? JSON.parse(savedObject.attributes.fieldAttrs)
: {};
@@ -406,6 +415,22 @@ export class IndexPatternsService {
},
spec.fieldAttrs
);
+ // APPLY RUNTIME FIELDS
+ for (const [key, value] of Object.entries(runtimeFieldMap || {})) {
+ if (spec.fields[key]) {
+ spec.fields[key].runtimeField = value;
+ } else {
+ spec.fields[key] = {
+ name: key,
+ type: castEsToKbnFieldTypeName(value.type),
+ runtimeField: value,
+ aggregatable: true,
+ searchable: true,
+ count: 0,
+ readFromDocValues: false,
+ };
+ }
+ }
} catch (err) {
if (err instanceof IndexPatternMissingIndices) {
this.onNotification({
diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts
index 9f9a26604a0e56..467b5125f03271 100644
--- a/src/plugins/data/common/index_patterns/types.ts
+++ b/src/plugins/data/common/index_patterns/types.ts
@@ -14,6 +14,14 @@ import { SerializedFieldFormat } from '../../../expressions/common';
import { KBN_FIELD_TYPES, IndexPatternField, FieldFormat } from '..';
export type FieldFormatMap = Record;
+const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const;
+type RuntimeType = typeof RUNTIME_FIELD_TYPES[number];
+export interface RuntimeField {
+ type: RuntimeType;
+ script: {
+ source: string;
+ };
+}
/**
* IIndexPattern allows for an IndexPattern OR an index pattern saved object
@@ -51,6 +59,7 @@ export interface IndexPatternAttributes {
sourceFilters?: string;
fieldFormatMap?: string;
fieldAttrs?: string;
+ runtimeFieldMap?: string;
/**
* prevents errors when index pattern exists before indices
*/
@@ -199,8 +208,10 @@ export interface FieldSpec {
subType?: IFieldSubType;
indexed?: boolean;
customLabel?: string;
+ runtimeField?: RuntimeField;
// not persisted
shortDotsEnable?: boolean;
+ isMapped?: boolean;
}
export type IndexPatternFieldMap = Record;
@@ -230,6 +241,7 @@ export interface IndexPatternSpec {
typeMeta?: TypeMeta;
type?: string;
fieldFormats?: Record;
+ runtimeFieldMap?: Record;
fieldAttrs?: FieldAttrs;
allowNoIndex?: boolean;
}
diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts
index 0b9c60e94a1985..6d7654c6659f23 100644
--- a/src/plugins/data/common/search/search_source/search_source.test.ts
+++ b/src/plugins/data/common/search/search_source/search_source.test.ts
@@ -20,6 +20,7 @@ const getComputedFields = () => ({
storedFields: [],
scriptFields: {},
docvalueFields: [],
+ runtimeFields: {},
});
const mockSource = { excludes: ['foo-*'] };
@@ -37,6 +38,13 @@ const indexPattern2 = ({
getSourceFiltering: () => mockSource2,
} as unknown) as IndexPattern;
+const runtimeFieldDef = {
+ type: 'keyword',
+ script: {
+ source: "emit('hello world')",
+ },
+};
+
describe('SearchSource', () => {
let mockSearchMethod: any;
let searchSourceDependencies: SearchSourceDependencies;
@@ -82,12 +90,14 @@ describe('SearchSource', () => {
describe('computed fields handling', () => {
test('still provides computed fields when no fields are specified', async () => {
+ const runtimeFields = { runtime_field: runtimeFieldDef };
searchSource.setField('index', ({
...indexPattern,
getComputedFields: () => ({
storedFields: ['hello'],
scriptFields: { world: {} },
docvalueFields: ['@timestamp'],
+ runtimeFields,
}),
} as unknown) as IndexPattern);
@@ -95,6 +105,7 @@ describe('SearchSource', () => {
expect(request.stored_fields).toEqual(['hello']);
expect(request.script_fields).toEqual({ world: {} });
expect(request.fields).toEqual(['@timestamp']);
+ expect(request.runtime_mappings).toEqual(runtimeFields);
});
test('never includes docvalue_fields', async () => {
@@ -390,15 +401,23 @@ describe('SearchSource', () => {
});
test('filters request when a specific list of fields is provided with fieldsFromSource', async () => {
+ const runtimeFields = { runtime_field: runtimeFieldDef, runtime_field_b: runtimeFieldDef };
searchSource.setField('index', ({
...indexPattern,
getComputedFields: () => ({
storedFields: ['*'],
scriptFields: { hello: {}, world: {} },
docvalueFields: ['@timestamp', 'date'],
+ runtimeFields,
}),
} as unknown) as IndexPattern);
- searchSource.setField('fieldsFromSource', ['hello', '@timestamp', 'foo-a', 'bar']);
+ searchSource.setField('fieldsFromSource', [
+ 'hello',
+ '@timestamp',
+ 'foo-a',
+ 'bar',
+ 'runtime_field',
+ ]);
const request = await searchSource.getSearchRequestBody();
expect(request._source).toEqual({
@@ -407,6 +426,7 @@ describe('SearchSource', () => {
expect(request.fields).toEqual(['@timestamp']);
expect(request.script_fields).toEqual({ hello: {} });
expect(request.stored_fields).toEqual(['@timestamp', 'bar']);
+ expect(request.runtime_mappings).toEqual({ runtime_field: runtimeFieldDef });
});
test('filters request when a specific list of fields is provided with fieldsFromSource or fields', async () => {
diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts
index 0f0688c9fc11f9..554e8385881f23 100644
--- a/src/plugins/data/common/search/search_source/search_source.ts
+++ b/src/plugins/data/common/search/search_source/search_source.ts
@@ -461,12 +461,13 @@ export class SearchSource {
searchRequest.indexType = this.getIndexType(index);
// get some special field types from the index pattern
- const { docvalueFields, scriptFields, storedFields } = index
+ const { docvalueFields, scriptFields, storedFields, runtimeFields } = index
? index.getComputedFields()
: {
docvalueFields: [],
scriptFields: {},
storedFields: ['*'],
+ runtimeFields: {},
};
const fieldListProvided = !!body.fields;
@@ -481,6 +482,7 @@ export class SearchSource {
...scriptFields,
};
body.stored_fields = storedFields;
+ body.runtime_mappings = runtimeFields || {};
// apply source filters from index pattern if specified by the user
let filteredDocvalueFields = docvalueFields;
@@ -518,13 +520,18 @@ export class SearchSource {
body.script_fields,
Object.keys(body.script_fields).filter((f) => uniqFieldNames.includes(f))
);
+ body.runtime_mappings = pick(
+ body.runtime_mappings,
+ Object.keys(body.runtime_mappings).filter((f) => uniqFieldNames.includes(f))
+ );
}
// request the remaining fields from stored_fields just in case, since the
// fields API does not handle stored fields
- const remainingFields = difference(uniqFieldNames, Object.keys(body.script_fields)).filter(
- Boolean
- );
+ const remainingFields = difference(uniqFieldNames, [
+ ...Object.keys(body.script_fields),
+ ...Object.keys(body.runtime_mappings),
+ ]).filter(Boolean);
// only include unique values
body.stored_fields = [...new Set(remainingFields)];
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index d6bd896a584a46..28997de4517e79 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -1265,6 +1265,7 @@ export type IMetricAggType = MetricAggType;
export class IndexPattern implements IIndexPattern {
// Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts
constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps);
+ addRuntimeField(name: string, runtimeField: RuntimeField): void;
addScriptedField(name: string, script: string, fieldType?: string): Promise;
readonly allowNoIndex: boolean;
// (undocumented)
@@ -1304,6 +1305,7 @@ export class IndexPattern implements IIndexPattern {
type: string | undefined;
typeMeta: string | undefined;
allowNoIndex: true | undefined;
+ runtimeFieldMap: string | undefined;
};
// (undocumented)
getComputedFields(): {
@@ -1313,6 +1315,7 @@ export class IndexPattern implements IIndexPattern {
field: any;
format: string;
}[];
+ runtimeFields: Record;
};
// (undocumented)
getFieldAttrs: () => {
@@ -1352,6 +1355,7 @@ export class IndexPattern implements IIndexPattern {
isTimeNanosBased(): boolean;
// (undocumented)
metaFields: string[];
+ removeRuntimeField(name: string): void;
removeScriptedField(fieldName: string): void;
resetOriginalSavedObjectBody: () => void;
// (undocumented)
@@ -1402,6 +1406,8 @@ export interface IndexPatternAttributes {
// (undocumented)
intervalName?: string;
// (undocumented)
+ runtimeFieldMap?: string;
+ // (undocumented)
sourceFilters?: string;
// (undocumented)
timeFieldName?: string;
@@ -1435,12 +1441,16 @@ export class IndexPatternField implements IFieldType {
get esTypes(): string[] | undefined;
// (undocumented)
get filterable(): boolean;
+ get isMapped(): boolean | undefined;
get lang(): string | undefined;
set lang(lang: string | undefined);
// (undocumented)
get name(): string;
// (undocumented)
get readFromDocValues(): boolean;
+ // (undocumented)
+ get runtimeField(): RuntimeField | undefined;
+ set runtimeField(runtimeField: RuntimeField | undefined);
get script(): string | undefined;
set script(script: string | undefined);
// (undocumented)
@@ -1537,6 +1547,8 @@ export interface IndexPatternSpec {
// @deprecated (undocumented)
intervalName?: string;
// (undocumented)
+ runtimeFieldMap?: Record;
+ // (undocumented)
sourceFilters?: SourceFilter[];
// (undocumented)
timeFieldName?: string;
@@ -2580,8 +2592,9 @@ export const UI_SETTINGS: {
// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrase_filter.ts:22:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrases_filter.ts:20:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts
-// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:63:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
-// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:133:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/search/aggs/types.ts:139:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/search/search_source/search_source.ts:186:7 - (ae-forgotten-export) The symbol "SearchFieldValue" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/field_formats/field_formats_service.ts:56:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/data/public/ui/query_string_input/_query_bar.scss b/src/plugins/data/public/ui/query_string_input/_query_bar.scss
index 3e3982dd58e57a..65f652df31d0c8 100644
--- a/src/plugins/data/public/ui/query_string_input/_query_bar.scss
+++ b/src/plugins/data/public/ui/query_string_input/_query_bar.scss
@@ -73,9 +73,10 @@
// sass-lint:disable-block no-important
flex-grow: 0 !important;
flex-basis: auto !important;
- margin-right: -$euiSizeXS !important;
&.kbnQueryBar__datePickerWrapper-isHidden {
+ // sass-lint:disable-block no-important
+ margin-right: -$euiSizeXS !important;
width: 0;
overflow: hidden;
max-width: 0;
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index ef8015ecaca26d..6a96fd8209a8d6 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -705,6 +705,7 @@ export type IMetricAggType = MetricAggType;
export class IndexPattern implements IIndexPattern {
// Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts
constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps);
+ addRuntimeField(name: string, runtimeField: RuntimeField): void;
addScriptedField(name: string, script: string, fieldType?: string): Promise;
readonly allowNoIndex: boolean;
// (undocumented)
@@ -746,6 +747,7 @@ export class IndexPattern implements IIndexPattern {
type: string | undefined;
typeMeta: string | undefined;
allowNoIndex: true | undefined;
+ runtimeFieldMap: string | undefined;
};
// (undocumented)
getComputedFields(): {
@@ -755,6 +757,7 @@ export class IndexPattern implements IIndexPattern {
field: any;
format: string;
}[];
+ runtimeFields: Record;
};
// (undocumented)
getFieldAttrs: () => {
@@ -796,6 +799,7 @@ export class IndexPattern implements IIndexPattern {
isTimeNanosBased(): boolean;
// (undocumented)
metaFields: string[];
+ removeRuntimeField(name: string): void;
removeScriptedField(fieldName: string): void;
resetOriginalSavedObjectBody: () => void;
// (undocumented)
@@ -838,6 +842,8 @@ export interface IndexPatternAttributes {
// (undocumented)
intervalName?: string;
// (undocumented)
+ runtimeFieldMap?: string;
+ // (undocumented)
sourceFilters?: string;
// (undocumented)
timeFieldName?: string;
@@ -1394,9 +1400,10 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
//
// src/plugins/data/common/es_query/filters/meta_filter.ts:42:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts
-// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:50:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts
-// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:63:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
-// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:133:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts
+// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:29:23 - (ae-forgotten-export) The symbol "buildCustomFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:29:23 - (ae-forgotten-export) The symbol "buildFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:46:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/discover/public/application/angular/directives/histogram.tsx b/src/plugins/discover/public/application/angular/directives/histogram.tsx
index ff10feea46d47c..b12de3f4496c5c 100644
--- a/src/plugins/discover/public/application/angular/directives/histogram.tsx
+++ b/src/plugins/discover/public/application/angular/directives/histogram.tsx
@@ -154,6 +154,10 @@ export class DiscoverHistogram extends Component
xAxisFormatter.convert(value)}
/>
{
// show all the Rows
$scope.minimumVisibleRows = $scope.hits;
// delay scrolling to after the rows have been rendered
- const bottomMarker = $element.find('#discoverBottomMarker');
- $timeout(() => {
- bottomMarker.focus();
- // The anchor tag is not technically empty (it's a hack to make Safari scroll)
- // so the browser will show a highlight: remove the focus once scrolled
- $timeout(() => {
- bottomMarker.blur();
- }, 0);
- }, 0);
+ const bottomMarker = document.getElementById('discoverBottomMarker');
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+
+ while ($scope.rows.length !== document.getElementsByClassName('kbnDocTable__row').length) {
+ await wait(50);
+ }
+ bottomMarker.focus();
+ await wait(50);
+ bottomMarker.blur();
};
$scope.newQuery = function () {
diff --git a/src/plugins/discover/public/application/angular/discover_state.test.ts b/src/plugins/discover/public/application/angular/discover_state.test.ts
index 45e5e252e8361b..809664de5f073c 100644
--- a/src/plugins/discover/public/application/angular/discover_state.test.ts
+++ b/src/plugins/discover/public/application/angular/discover_state.test.ts
@@ -101,8 +101,9 @@ describe('Test discover state with legacy migration', () => {
describe('createSearchSessionRestorationDataProvider', () => {
let mockSavedSearch: SavedSearch = ({} as unknown) as SavedSearch;
+ const mockDataPlugin = dataPluginMock.createStartContract();
const searchSessionInfoProvider = createSearchSessionRestorationDataProvider({
- data: dataPluginMock.createStartContract(),
+ data: mockDataPlugin,
appStateContainer: getState({
history: createBrowserHistory(),
}).appStateContainer,
@@ -124,4 +125,30 @@ describe('createSearchSessionRestorationDataProvider', () => {
expect(await searchSessionInfoProvider.getName()).toBe('Discover');
});
});
+
+ describe('session state', () => {
+ test('restoreState has sessionId and initialState has not', async () => {
+ const searchSessionId = 'id';
+ (mockDataPlugin.search.session.getSessionId as jest.Mock).mockImplementation(
+ () => searchSessionId
+ );
+ const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData();
+ expect(initialState.searchSessionId).toBeUndefined();
+ expect(restoreState.searchSessionId).toBe(searchSessionId);
+ });
+
+ test('restoreState has absoluteTimeRange', async () => {
+ const relativeTime = 'relativeTime';
+ const absoluteTime = 'absoluteTime';
+ (mockDataPlugin.query.timefilter.timefilter.getTime as jest.Mock).mockImplementation(
+ () => relativeTime
+ );
+ (mockDataPlugin.query.timefilter.timefilter.getAbsoluteTime as jest.Mock).mockImplementation(
+ () => absoluteTime
+ );
+ const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData();
+ expect(initialState.timeRange).toBe(relativeTime);
+ expect(restoreState.timeRange).toBe(absoluteTime);
+ });
+ });
});
diff --git a/src/plugins/discover/public/application/angular/discover_state.ts b/src/plugins/discover/public/application/angular/discover_state.ts
index fe05fceb858e5a..c769e263655abd 100644
--- a/src/plugins/discover/public/application/angular/discover_state.ts
+++ b/src/plugins/discover/public/application/angular/discover_state.ts
@@ -275,12 +275,12 @@ export function createSearchSessionRestorationDataProvider(deps: {
initialState: createUrlGeneratorState({
...deps,
getSavedSearchId,
- forceAbsoluteTime: false,
+ shouldRestoreSearchSession: false,
}),
restoreState: createUrlGeneratorState({
...deps,
getSavedSearchId,
- forceAbsoluteTime: true,
+ shouldRestoreSearchSession: true,
}),
};
},
@@ -291,15 +291,12 @@ function createUrlGeneratorState({
appStateContainer,
data,
getSavedSearchId,
- forceAbsoluteTime,
+ shouldRestoreSearchSession,
}: {
appStateContainer: StateContainer;
data: DataPublicPluginStart;
getSavedSearchId: () => string | undefined;
- /**
- * Can force time range from time filter to convert from relative to absolute time range
- */
- forceAbsoluteTime: boolean;
+ shouldRestoreSearchSession: boolean;
}): DiscoverUrlGeneratorState {
const appState = appStateContainer.get();
return {
@@ -307,10 +304,10 @@ function createUrlGeneratorState({
indexPatternId: appState.index,
query: appState.query,
savedSearchId: getSavedSearchId(),
- timeRange: forceAbsoluteTime
+ timeRange: shouldRestoreSearchSession
? data.query.timefilter.timefilter.getAbsoluteTime()
: data.query.timefilter.timefilter.getTime(),
- searchSessionId: data.search.session.getSessionId(),
+ searchSessionId: shouldRestoreSearchSession ? data.search.session.getSessionId() : undefined,
columns: appState.columns,
sort: appState.sort,
savedQuery: appState.savedQuery,
diff --git a/src/plugins/discover/public/application/angular/helpers/point_series.ts b/src/plugins/discover/public/application/angular/helpers/point_series.ts
index db58aebfd3ad7d..89317f77ef0606 100644
--- a/src/plugins/discover/public/application/angular/helpers/point_series.ts
+++ b/src/plugins/discover/public/application/angular/helpers/point_series.ts
@@ -62,6 +62,7 @@ export interface Chart {
}>;
xAxisOrderedValues: number[];
xAxisFormat: Dimension['format'];
+ yAxisFormat: Dimension['format'];
xAxisLabel: Column['name'];
yAxisLabel?: Column['name'];
ordered: Ordered;
@@ -76,7 +77,7 @@ export const buildPointSeriesData = (table: Table, dimensions: Dimensions) => {
chart.xAxisOrderedValues = uniq(table.rows.map((r) => r[xAccessor] as number));
chart.xAxisFormat = x.format;
chart.xAxisLabel = table.columns[x.accessor].name;
-
+ chart.yAxisFormat = y.format;
const { intervalESUnit, intervalESValue, interval, bounds } = x.params;
chart.ordered = {
date: true,
diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts
index f72d65dd2ee566..1394ceab1dd18e 100644
--- a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts
+++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts
@@ -49,6 +49,7 @@ describe('getSharingData', () => {
"should": Array [],
},
},
+ "runtime_mappings": Object {},
"script_fields": Object {},
"sort": Array [
Object {
diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts
index b7f7a5abb82b01..e2e1d7f05851ca 100644
--- a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts
@@ -13,14 +13,24 @@ import { IKibanaResponse, KibanaResponseFactory } from 'kibana/server';
interface EsErrorHandlerParams {
error: ApiError;
response: KibanaResponseFactory;
+ handleCustomError?: () => IKibanaResponse;
}
/*
* For errors returned by the new elasticsearch js client.
*/
-export const handleEsError = ({ error, response }: EsErrorHandlerParams): IKibanaResponse => {
+export const handleEsError = ({
+ error,
+ response,
+ handleCustomError,
+}: EsErrorHandlerParams): IKibanaResponse => {
// error.name is slightly better in terms of performance, since all errors now have name property
if (error.name === 'ResponseError') {
+ // The consumer may sometimes want to provide a custom response
+ if (typeof handleCustomError === 'function') {
+ return handleCustomError();
+ }
+
const { statusCode, body } = error as ResponseError;
return response.customError({
statusCode,
diff --git a/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap b/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap
index 9207c6467f6a92..151bd91750daa3 100644
--- a/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap
+++ b/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap
@@ -170,6 +170,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
@@ -914,6 +918,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
@@ -1607,6 +1615,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = `
@@ -1824,6 +1836,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
@@ -2816,6 +2832,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = `
@@ -3029,6 +3049,7 @@ exports[`CronEditor is rendered with a MINUTE frequency 1`] = `
@@ -3241,6 +3265,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
@@ -4173,6 +4201,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
@@ -4568,6 +4600,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
@@ -5261,6 +5297,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = `
@@ -5478,6 +5518,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
@@ -6074,6 +6118,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
@@ -6469,6 +6517,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
@@ -7162,6 +7214,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = `
@@ -7379,6 +7435,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
@@ -8174,6 +8234,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
@@ -8628,6 +8692,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
@@ -9023,6 +9091,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
@@ -9716,6 +9788,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = `
diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts
index 8e068818ec0ce7..0240ec90cb1e63 100644
--- a/src/plugins/expressions/common/execution/execution.ts
+++ b/src/plugins/expressions/common/execution/execution.ts
@@ -29,6 +29,7 @@ import { getByAlias } from '../util/get_by_alias';
import { ExecutionContract } from './execution_contract';
import { ExpressionExecutionParams } from '../service';
import { TablesAdapter } from '../util/tables_adapter';
+import { ExpressionsInspectorAdapter } from '../util/expressions_inspector_adapter';
/**
* AbortController is not available in Node until v15, so we
@@ -63,6 +64,7 @@ export interface ExecutionParams {
const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
requests: new RequestAdapter(),
tables: new TablesAdapter(),
+ expression: new ExpressionsInspectorAdapter(),
});
export class Execution<
@@ -208,6 +210,9 @@ export class Execution<
this.firstResultFuture.promise
.then(
(result) => {
+ if (this.context.inspectorAdapters.expression) {
+ this.context.inspectorAdapters.expression.logAST(this.state.get().ast);
+ }
this.state.transitions.setResult(result);
},
(error) => {
diff --git a/examples/state_containers_examples/server/index.ts b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts
similarity index 52%
rename from examples/state_containers_examples/server/index.ts
rename to src/plugins/expressions/common/util/expressions_inspector_adapter.ts
index 6ae5d240667115..c82884d373d2f9 100644
--- a/examples/state_containers_examples/server/index.ts
+++ b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts
@@ -6,12 +6,17 @@
* Public License, v 1.
*/
-import { PluginInitializerContext } from '../../../src/core/server';
-import { StateDemoServerPlugin } from './plugin';
+import { EventEmitter } from 'events';
-export function plugin(initializerContext: PluginInitializerContext) {
- return new StateDemoServerPlugin(initializerContext);
-}
+export class ExpressionsInspectorAdapter extends EventEmitter {
+ private _ast: any = {};
+
+ public logAST(ast: any): void {
+ this._ast = ast;
+ this.emit('change', this._ast);
+ }
-export { StateDemoServerPlugin as Plugin };
-export * from '../common';
+ public get ast() {
+ return this._ast;
+ }
+}
diff --git a/src/plugins/expressions/common/util/index.ts b/src/plugins/expressions/common/util/index.ts
index ecb7d5cdca81e7..4762f9979fe4a0 100644
--- a/src/plugins/expressions/common/util/index.ts
+++ b/src/plugins/expressions/common/util/index.ts
@@ -9,3 +9,4 @@
export * from './create_error';
export * from './get_by_alias';
export * from './tables_adapter';
+export * from './expressions_inspector_adapter';
diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts
index 9485daf49c981f..d6dd2fc1f3d376 100644
--- a/src/plugins/expressions/public/index.ts
+++ b/src/plugins/expressions/public/index.ts
@@ -107,4 +107,5 @@ export {
ExpressionsServiceSetup,
ExpressionsServiceStart,
TablesAdapter,
+ ExpressionsInspectorAdapter,
} from '../common';
diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md
index 7fa0857be8aba9..029d727e82e740 100644
--- a/src/plugins/expressions/public/public.api.md
+++ b/src/plugins/expressions/public/public.api.md
@@ -551,6 +551,16 @@ export class ExpressionRenderHandler {
update$: Observable;
}
+// Warning: (ae-missing-release-tag) "ExpressionsInspectorAdapter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export class ExpressionsInspectorAdapter extends EventEmitter {
+ // (undocumented)
+ get ast(): any;
+ // (undocumented)
+ logAST(ast: any): void;
+}
+
// Warning: (ae-missing-release-tag) "ExpressionsPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
diff --git a/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap
index 1484ce6b1a81f8..d38c77faab7f83 100644
--- a/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap
+++ b/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap
@@ -51,6 +51,7 @@ exports[`should render popover when appLinks is not empty 1`] = `
},
]
}
+ size="m"
/>
`;
diff --git a/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx b/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
index ec9b5ee7e43dad..4d82571995d58e 100644
--- a/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
+++ b/src/plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
@@ -29,8 +29,6 @@ beforeEach(() => {
name: 'test',
title: 'test',
visualization: null,
- requestHandler: 'test',
- responseHandler: 'test',
stage: 'beta',
requiresSearch: false,
hidden: false,
diff --git a/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx b/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx
index a2ae1e6851a6bd..5e432d81ebefb0 100644
--- a/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx
+++ b/src/plugins/input_control_vis/public/components/editor/controls_tab.tsx
@@ -19,7 +19,7 @@ import {
EuiSelect,
} from '@elastic/eui';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { IIndexPattern } from 'src/plugins/data/public';
import { ControlEditor } from './control_editor';
import {
@@ -40,7 +40,7 @@ interface ControlsTabUiState {
type: CONTROL_TYPES;
}
-export type ControlsTabProps = VisOptionsProps & {
+export type ControlsTabProps = VisEditorOptionsProps & {
deps: InputControlVisDependencies;
};
diff --git a/src/plugins/input_control_vis/public/components/editor/index.tsx b/src/plugins/input_control_vis/public/components/editor/index.tsx
index 15b1039a8cb3ce..ee457a47a3b6df 100644
--- a/src/plugins/input_control_vis/public/components/editor/index.tsx
+++ b/src/plugins/input_control_vis/public/components/editor/index.tsx
@@ -7,7 +7,7 @@
*/
import React, { lazy } from 'react';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { InputControlVisDependencies } from '../../plugin';
import { InputControlVisParams } from '../../types';
@@ -15,9 +15,9 @@ const ControlsTab = lazy(() => import('./controls_tab'));
const OptionsTab = lazy(() => import('./options_tab'));
export const getControlsTab = (deps: InputControlVisDependencies) => (
- props: VisOptionsProps
+ props: VisEditorOptionsProps
) => ;
-export const OptionsTabLazy = (props: VisOptionsProps) => (
+export const OptionsTabLazy = (props: VisEditorOptionsProps) => (
);
diff --git a/src/plugins/input_control_vis/public/components/editor/options_tab.tsx b/src/plugins/input_control_vis/public/components/editor/options_tab.tsx
index fa995be0840ce3..9a7eaa71c33248 100644
--- a/src/plugins/input_control_vis/public/components/editor/options_tab.tsx
+++ b/src/plugins/input_control_vis/public/components/editor/options_tab.tsx
@@ -12,10 +12,10 @@ import { EuiForm, EuiFormRow, EuiSwitch } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiSwitchEvent } from '@elastic/eui';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { InputControlVisParams } from '../../types';
-export type OptionsTabProps = VisOptionsProps;
+export type OptionsTabProps = VisEditorOptionsProps;
class OptionsTab extends PureComponent {
handleUpdateFiltersChange = (event: EuiSwitchEvent) => {
diff --git a/src/plugins/input_control_vis/public/input_control_vis_type.ts b/src/plugins/input_control_vis/public/input_control_vis_type.ts
index 8d8fdf2cfc5680..00e680aa5d3289 100644
--- a/src/plugins/input_control_vis/public/input_control_vis_type.ts
+++ b/src/plugins/input_control_vis/public/input_control_vis_type.ts
@@ -7,7 +7,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { VisGroups, BaseVisTypeOptions } from '../../visualizations/public';
+import { VisGroups, VisTypeDefinition } from '../../visualizations/public';
import { getControlsTab, OptionsTabLazy } from './components/editor';
import { InputControlVisDependencies } from './plugin';
import { toExpressionAst } from './to_ast';
@@ -15,7 +15,7 @@ import { InputControlVisParams } from './types';
export function createInputControlVisTypeDefinition(
deps: InputControlVisDependencies
-): BaseVisTypeOptions {
+): VisTypeDefinition {
const ControlsTab = getControlsTab(deps);
return {
@@ -56,7 +56,6 @@ export function createInputControlVisTypeDefinition(
],
},
inspectorAdapters: {},
- requestHandler: 'none',
toExpressionAst,
};
}
diff --git a/x-pack/plugins/xpack_legacy/common/eui_styled_components.tsx b/src/plugins/kibana_react/common/eui_styled_components.tsx
similarity index 86%
rename from x-pack/plugins/xpack_legacy/common/eui_styled_components.tsx
rename to src/plugins/kibana_react/common/eui_styled_components.tsx
index aab16f9d79c4b0..fe002500309ad2 100644
--- a/x-pack/plugins/xpack_legacy/common/eui_styled_components.tsx
+++ b/src/plugins/kibana_react/common/eui_styled_components.tsx
@@ -1,7 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
*/
import React from 'react';
diff --git a/src/plugins/kibana_react/public/use_url_tracker/index.ts b/src/plugins/kibana_react/common/index.ts
similarity index 87%
rename from src/plugins/kibana_react/public/use_url_tracker/index.ts
rename to src/plugins/kibana_react/common/index.ts
index 7ba21ddafaef21..0af8ed573699b2 100644
--- a/src/plugins/kibana_react/public/use_url_tracker/index.ts
+++ b/src/plugins/kibana_react/common/index.ts
@@ -6,4 +6,4 @@
* Public License, v 1.
*/
-export { useUrlTracker } from './use_url_tracker';
+export * from './eui_styled_components';
diff --git a/src/plugins/kibana_react/kibana.json b/src/plugins/kibana_react/kibana.json
index c05490c3499170..6bf7ff1d82070d 100644
--- a/src/plugins/kibana_react/kibana.json
+++ b/src/plugins/kibana_react/kibana.json
@@ -3,5 +3,5 @@
"version": "kibana",
"ui": true,
"server": false,
- "requiredBundles": ["kibanaUtils"]
+ "extraPublicDirs": ["common"]
}
diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts
index 4ec96f1db81995..cbb60bba478618 100644
--- a/src/plugins/kibana_react/public/index.ts
+++ b/src/plugins/kibana_react/public/index.ts
@@ -15,13 +15,13 @@ export * from './ui_settings';
export * from './field_icon';
export * from './field_button';
export * from './table_list_view';
+export * from './toolbar_button';
export * from './split_panel';
export * from './react_router_navigate';
export { ValidatedDualRange, Value } from './validated_range';
export * from './notifications';
export { Markdown, MarkdownSimple } from './markdown';
export { reactToUiComponent, uiToReactComponent } from './adapters';
-export { useUrlTracker } from './use_url_tracker';
export { toMountPoint, MountPointPortal } from './util';
export { RedirectAppLinks } from './app_links';
diff --git a/src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap b/src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap
new file mode 100644
index 00000000000000..294be46398e8a2
--- /dev/null
+++ b/src/plugins/kibana_react/public/toolbar_button/__snapshots__/toolbar_button.test.tsx.snap
@@ -0,0 +1,199 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`font weights bold is applied 1`] = `
+
+`;
+
+exports[`font weights normal is applied 1`] = `
+
+`;
+
+exports[`hasArrow is rendered 1`] = `
+
+`;
+
+exports[`positions center is applied 1`] = `
+
+`;
+
+exports[`positions left is applied 1`] = `
+
+`;
+
+exports[`positions none is applied 1`] = `
+
+`;
+
+exports[`positions right is applied 1`] = `
+
+`;
+
+exports[`sizes m is applied 1`] = `
+
+`;
+
+exports[`sizes s is applied 1`] = `
+
+`;
diff --git a/src/plugins/kibana_react/public/toolbar_button/index.ts b/src/plugins/kibana_react/public/toolbar_button/index.ts
new file mode 100644
index 00000000000000..f952741291b684
--- /dev/null
+++ b/src/plugins/kibana_react/public/toolbar_button/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+export * from './toolbar_button';
diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_button.scss b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.scss
similarity index 81%
rename from x-pack/plugins/lens/public/shared_components/toolbar_button.scss
rename to src/plugins/kibana_react/public/toolbar_button/toolbar_button.scss
index 61b02f47678c3d..f290b3c7c5f892 100644
--- a/x-pack/plugins/lens/public/shared_components/toolbar_button.scss
+++ b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.scss
@@ -1,4 +1,4 @@
-.lnsToolbarButton {
+.kbnToolbarButton {
line-height: $euiButtonHeight; // Keeps alignment of text and chart icon
background-color: $euiColorEmptyShade;
@@ -15,11 +15,11 @@
pointer-events: initial;
}
- .lnsToolbarButton__text > svg {
+ .kbnToolbarButton__text > svg {
margin-top: -1px; // Just some weird alignment issue when icon is the child not the `iconType`
}
- .lnsToolbarButton__text:empty {
+ .kbnToolbarButton__text:empty {
margin: 0;
}
@@ -27,34 +27,33 @@
&[class*='fullWidth'] {
text-align: left;
- .lnsToolbarButton__content {
+ .kbnToolbarButton__content {
justify-content: space-between;
}
}
-
}
-.lnsToolbarButton--groupLeft {
+.kbnToolbarButton--groupLeft {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
-.lnsToolbarButton--groupCenter {
+.kbnToolbarButton--groupCenter {
border-radius: 0;
border-left: none;
}
-.lnsToolbarButton--groupRight {
+.kbnToolbarButton--groupRight {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left: none;
}
-.lnsToolbarButton--bold {
+.kbnToolbarButton--bold {
font-weight: $euiFontWeightBold;
}
-.lnsToolbarButton--s {
+.kbnToolbarButton--s {
box-shadow: none !important; // sass-lint:disable-line no-important
font-size: $euiFontSizeS;
}
diff --git a/src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx
new file mode 100644
index 00000000000000..3d4ce29ffa5e9b
--- /dev/null
+++ b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.test.tsx
@@ -0,0 +1,47 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { ToolbarButton, POSITIONS, WEIGHTS, TOOLBAR_BUTTON_SIZES } from './toolbar_button';
+
+const noop = () => {};
+
+describe('sizes', () => {
+ TOOLBAR_BUTTON_SIZES.forEach((size) => {
+ test(`${size} is applied`, () => {
+ const component = shallow();
+ expect(component).toMatchSnapshot();
+ });
+ });
+});
+
+describe('positions', () => {
+ POSITIONS.forEach((position) => {
+ test(`${position} is applied`, () => {
+ const component = shallow();
+ expect(component).toMatchSnapshot();
+ });
+ });
+});
+
+describe('font weights', () => {
+ WEIGHTS.forEach((weight) => {
+ test(`${weight} is applied`, () => {
+ const component = shallow();
+ expect(component).toMatchSnapshot();
+ });
+ });
+});
+
+describe('hasArrow', () => {
+ it('is rendered', () => {
+ const component = shallow();
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_button.tsx b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.tsx
similarity index 61%
rename from x-pack/plugins/lens/public/shared_components/toolbar_button.tsx
rename to src/plugins/kibana_react/public/toolbar_button/toolbar_button.tsx
index 2ba227e6ff84f4..388a11992268eb 100644
--- a/x-pack/plugins/lens/public/shared_components/toolbar_button.tsx
+++ b/src/plugins/kibana_react/public/toolbar_button/toolbar_button.tsx
@@ -1,7 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
*/
import './toolbar_button.scss';
@@ -11,16 +13,24 @@ import { EuiButton, PropsOf, EuiButtonProps } from '@elastic/eui';
const groupPositionToClassMap = {
none: null,
- left: 'lnsToolbarButton--groupLeft',
- center: 'lnsToolbarButton--groupCenter',
- right: 'lnsToolbarButton--groupRight',
+ left: 'toolbarButton--groupLeft',
+ center: 'toolbarButton--groupCenter',
+ right: 'toolbarButton--groupRight',
};
+type ButtonPositions = keyof typeof groupPositionToClassMap;
+export const POSITIONS = Object.keys(groupPositionToClassMap) as ButtonPositions[];
+
+type Weights = 'normal' | 'bold';
+export const WEIGHTS = ['normal', 'bold'] as Weights[];
+
+export const TOOLBAR_BUTTON_SIZES: Array = ['s', 'm'];
+
export type ToolbarButtonProps = PropsOf & {
/**
* Determines prominence
*/
- fontWeight?: 'normal' | 'bold';
+ fontWeight?: Weights;
/**
* Smaller buttons also remove extra shadow for less prominence
*/
@@ -32,7 +42,7 @@ export type ToolbarButtonProps = PropsOf & {
/**
* Adjusts the borders for groupings
*/
- groupPosition?: 'none' | 'left' | 'center' | 'right';
+ groupPosition?: ButtonPositions;
dataTestSubj?: string;
};
@@ -47,9 +57,9 @@ export const ToolbarButton: React.FunctionComponent = ({
...rest
}) => {
const classes = classNames(
- 'lnsToolbarButton',
+ 'kbnToolbarButton',
groupPositionToClassMap[groupPosition],
- [`lnsToolbarButton--${fontWeight}`, `lnsToolbarButton--${size}`],
+ [`kbnToolbarButton--${fontWeight}`, `kbnToolbarButton--${size}`],
className
);
return (
@@ -60,10 +70,10 @@ export const ToolbarButton: React.FunctionComponent = ({
iconType={hasArrow ? 'arrowDown' : ''}
color="text"
contentProps={{
- className: 'lnsToolbarButton__content',
+ className: 'kbnToolbarButton__content',
}}
textProps={{
- className: 'lnsToolbarButton__text',
+ className: 'kbnToolbarButton__text',
}}
{...rest}
size={size}
diff --git a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.test.tsx b/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.test.tsx
deleted file mode 100644
index ed3eca04943a6f..00000000000000
--- a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.test.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { renderHook } from '@testing-library/react-hooks';
-import { useUrlTracker } from './use_url_tracker';
-import { StubBrowserStorage } from '@kbn/test/jest';
-import { createMemoryHistory } from 'history';
-
-describe('useUrlTracker', () => {
- const key = 'key';
- let storage = new StubBrowserStorage();
- let history = createMemoryHistory();
- beforeEach(() => {
- storage = new StubBrowserStorage();
- history = createMemoryHistory();
- });
-
- it('should track history changes and save them to storage', () => {
- expect(storage.getItem(key)).toBeNull();
- const { unmount } = renderHook(() => {
- useUrlTracker(key, history, () => false, storage);
- });
- expect(storage.getItem(key)).toBe('/');
- history.push('/change');
- expect(storage.getItem(key)).toBe('/change');
- unmount();
- history.push('/other-change');
- expect(storage.getItem(key)).toBe('/change');
- });
-
- it('by default should restore initial url', () => {
- storage.setItem(key, '/change');
- renderHook(() => {
- useUrlTracker(key, history, undefined, storage);
- });
- expect(history.location.pathname).toBe('/change');
- });
-
- it('should restore initial url if shouldRestoreUrl cb returns true', () => {
- storage.setItem(key, '/change');
- renderHook(() => {
- useUrlTracker(key, history, () => true, storage);
- });
- expect(history.location.pathname).toBe('/change');
- });
-
- it('should not restore initial url if shouldRestoreUrl cb returns false', () => {
- storage.setItem(key, '/change');
- renderHook(() => {
- useUrlTracker(key, history, () => false, storage);
- });
- expect(history.location.pathname).toBe('/');
- });
-});
diff --git a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.tsx b/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.tsx
deleted file mode 100644
index 5f3caf03ae447a..00000000000000
--- a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { History } from 'history';
-import { useLayoutEffect } from 'react';
-import { createUrlTracker } from '../../../kibana_utils/public/';
-
-/**
- * State management url_tracker in react hook form
- *
- * Replicates what src/legacy/ui/public/chrome/api/nav.ts did
- * Persists the url in sessionStorage so it could be restored if navigated back to the app
- *
- * @param key - key to use in storage
- * @param history - history instance to use
- * @param shouldRestoreUrl - cb if url should be restored
- * @param storage - storage to use. window.sessionStorage is default
- */
-export function useUrlTracker(
- key: string,
- history: History,
- shouldRestoreUrl: (urlToRestore: string) => boolean = () => true,
- storage: Storage = sessionStorage
-) {
- useLayoutEffect(() => {
- const urlTracker = createUrlTracker(key, storage);
- const urlToRestore = urlTracker.getTrackedUrl();
- if (urlToRestore && shouldRestoreUrl(urlToRestore)) {
- history.replace(urlToRestore);
- }
- const stopTrackingUrl = urlTracker.startTrackingUrl(history);
- return () => {
- stopTrackingUrl();
- };
- }, [key, history]);
-}
diff --git a/src/plugins/kibana_react/tsconfig.json b/src/plugins/kibana_react/tsconfig.json
index 52899f868dbfbb..eb9a24ca141f6e 100644
--- a/src/plugins/kibana_react/tsconfig.json
+++ b/src/plugins/kibana_react/tsconfig.json
@@ -7,11 +7,6 @@
"declaration": true,
"declarationMap": true
},
- "include": [
- "public/**/*",
- "../../../typings/**/*"
- ],
- "references": [
- { "path": "../kibana_utils/tsconfig.json" }
- ]
+ "include": ["common/**/*", "public/**/*", "../../../typings/**/*"],
+ "references": [{ "path": "../kibana_utils/tsconfig.json" }]
}
diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
index d75b2981035f43..28eeb461f7a86c 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
@@ -77,7 +77,6 @@ export const stackManagementSchema: MakeSchemaFrom = {
'sort:options': { type: 'keyword' },
'savedObjects:listingLimit': { type: 'long' },
'query:queryString:options': { type: 'keyword' },
- pageNavigation: { type: 'keyword' },
'metrics:max_buckets': { type: 'long' },
'query:allowLeadingWildcards': { type: 'boolean' },
metaFields: { type: 'keyword' }, // it's an array
diff --git a/src/plugins/region_map/public/components/index.tsx b/src/plugins/region_map/public/components/index.tsx
index 2ad3c0ffec3f47..187c36f9cc46dc 100644
--- a/src/plugins/region_map/public/components/index.tsx
+++ b/src/plugins/region_map/public/components/index.tsx
@@ -8,11 +8,11 @@
import React, { lazy } from 'react';
import { IServiceSettings } from 'src/plugins/maps_legacy/public';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { RegionMapVisParams } from '../region_map_types';
const RegionMapOptions = lazy(() => import('./region_map_options'));
export const createRegionMapOptions = (getServiceSettings: () => Promise) => (
- props: VisOptionsProps
+ props: VisEditorOptionsProps
) => ;
diff --git a/src/plugins/region_map/public/components/region_map_options.tsx b/src/plugins/region_map/public/components/region_map_options.tsx
index 0b11e1c5164d3b..43bb15a547435d 100644
--- a/src/plugins/region_map/public/components/region_map_options.tsx
+++ b/src/plugins/region_map/public/components/region_map_options.tsx
@@ -10,7 +10,7 @@ import React, { useCallback, useMemo } from 'react';
import { EuiIcon, EuiLink, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { FileLayerField, VectorLayer, IServiceSettings } from '../../../maps_legacy/public';
import { SelectOption, SwitchOption, NumberInputOption } from '../../../vis_default_editor/public';
import { WmsOptions } from '../../../maps_legacy/public';
@@ -28,7 +28,7 @@ const mapFieldForOption = ({ description, name }: FileLayerField) => ({
export type RegionMapOptionsProps = {
getServiceSettings: () => Promise;
-} & VisOptionsProps;
+} & VisEditorOptionsProps;
function RegionMapOptions(props: RegionMapOptionsProps) {
const { getServiceSettings, stateParams, vis, setValue } = props;
diff --git a/src/plugins/region_map/public/region_map_type.ts b/src/plugins/region_map/public/region_map_type.ts
index bda478389faa12..c1d46cd603f0b6 100644
--- a/src/plugins/region_map/public/region_map_type.ts
+++ b/src/plugins/region_map/public/region_map_type.ts
@@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';
-import { BaseVisTypeOptions } from '../../visualizations/public';
+import { VisTypeDefinition } from '../../visualizations/public';
import { truncatedColorSchemas } from '../../charts/public';
import { ORIGIN } from '../../maps_legacy/public';
@@ -23,7 +23,7 @@ export function createRegionMapTypeDefinition({
uiSettings,
regionmapsConfig,
getServiceSettings,
-}: RegionMapVisualizationDependencies): BaseVisTypeOptions {
+}: RegionMapVisualizationDependencies): VisTypeDefinition {
return {
name: 'region_map',
getInfoMessage: getDeprecationMessage,
@@ -139,5 +139,6 @@ provided base maps, or add your own. Darker colors represent higher values.',
return vis;
},
+ requiresSearch: true,
};
}
diff --git a/src/plugins/region_map/public/to_ast.ts b/src/plugins/region_map/public/to_ast.ts
index 7c81b8fa9ecbbf..77aaf18f0f6975 100644
--- a/src/plugins/region_map/public/to_ast.ts
+++ b/src/plugins/region_map/public/to_ast.ts
@@ -11,11 +11,11 @@ import {
IndexPatternLoadExpressionFunctionDefinition,
} from '../../data/public';
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
-import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public';
+import { getVisSchemas, VisToExpressionAst } from '../../visualizations/public';
import { RegionMapExpressionFunctionDefinition } from './region_map_fn';
import { RegionMapVisConfig, RegionMapVisParams } from './region_map_types';
-export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => {
+export const toExpressionAst: VisToExpressionAst = (vis, params) => {
const esaggs = buildExpressionFunction('esaggs', {
index: buildExpression([
buildExpressionFunction('indexPatternLoad', {
diff --git a/src/plugins/saved_objects_management/public/lib/create_field_list.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.ts
index cd30a02bd0ef38..4497fb04ffa2c2 100644
--- a/src/plugins/saved_objects_management/public/lib/create_field_list.ts
+++ b/src/plugins/saved_objects_management/public/lib/create_field_list.ts
@@ -11,11 +11,12 @@ import { SimpleSavedObject } from '../../../../core/public';
import { castEsToKbnFieldTypeName } from '../../../data/public';
import { ObjectField } from '../management_section/types';
import { SavedObjectLoader } from '../../../saved_objects/public';
+import { SavedObjectWithMetadata } from '../types';
const maxRecursiveIterations = 20;
export function createFieldList(
- object: SimpleSavedObject,
+ object: SimpleSavedObject | SavedObjectWithMetadata,
service?: SavedObjectLoader
): ObjectField[] {
let fields = Object.entries(object.attributes as Record).reduce(
diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx
index 96a4a24f6591e1..e048b92b9566c1 100644
--- a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx
@@ -19,14 +19,15 @@ import { set } from '@elastic/safer-lodash-set';
import { cloneDeep } from 'lodash';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { SimpleSavedObject, SavedObjectsClientContract } from '../../../../../../core/public';
+import { SavedObjectsClientContract } from '../../../../../../core/public';
import { SavedObjectLoader } from '../../../../../saved_objects/public';
import { Field } from './field';
import { ObjectField, FieldState, SubmittedFormData } from '../../types';
import { createFieldList } from '../../../lib';
+import { SavedObjectWithMetadata } from '../../../types';
interface FormProps {
- object: SimpleSavedObject;
+ object: SavedObjectWithMetadata;
service: SavedObjectLoader;
savedObjectsClient: SavedObjectsClientContract;
editionEnabled: boolean;
diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx
index 31c0a76e16f589..3343e0a63f54c7 100644
--- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx
@@ -14,16 +14,18 @@ import {
SavedObjectsClientContract,
OverlayStart,
NotificationsStart,
- SimpleSavedObject,
ScopedHistory,
+ HttpSetup,
} from '../../../../../core/public';
import { ISavedObjectsManagementServiceRegistry } from '../../services';
import { Header, NotFoundErrors, Intro, Form } from './components';
-import { canViewInApp } from '../../lib';
+import { canViewInApp, findObject } from '../../lib';
import { SubmittedFormData } from '../types';
+import { SavedObjectWithMetadata } from '../../types';
interface SavedObjectEditionProps {
id: string;
+ http: HttpSetup;
serviceName: string;
serviceRegistry: ISavedObjectsManagementServiceRegistry;
capabilities: Capabilities;
@@ -36,7 +38,7 @@ interface SavedObjectEditionProps {
interface SavedObjectEditionState {
type: string;
- object?: SimpleSavedObject;
+ object?: SavedObjectWithMetadata;
}
export class SavedObjectEdition extends Component<
@@ -56,9 +58,9 @@ export class SavedObjectEdition extends Component<
}
componentDidMount() {
- const { id, savedObjectsClient } = this.props;
+ const { http, id } = this.props;
const { type } = this.state;
- savedObjectsClient.get(type, id).then((object) => {
+ findObject(http, type, id).then((object) => {
this.setState({
object,
});
@@ -70,7 +72,7 @@ export class SavedObjectEdition extends Component<
capabilities,
notFoundType,
serviceRegistry,
- id,
+ http,
serviceName,
savedObjectsClient,
} = this.props;
@@ -80,7 +82,7 @@ export class SavedObjectEdition extends Component<
string,
boolean
>;
- const canView = canViewInApp(capabilities, type);
+ const canView = canViewInApp(capabilities, type) && Boolean(object?.meta.inAppUrl?.path);
const service = serviceRegistry.get(serviceName)!.service;
return (
@@ -91,7 +93,7 @@ export class SavedObjectEdition extends Component<
canViewInApp={canView}
type={type}
onDeleteClick={() => this.delete()}
- viewUrl={service.urlFor(id)}
+ viewUrl={http.basePath.prepend(object?.meta.inAppUrl?.path || '')}
/>
{notFoundType && (
<>
diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx
index 758789aa0f47e1..2af7c22488c515 100644
--- a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx
@@ -11,6 +11,7 @@ import { useParams, useLocation } from 'react-router-dom';
import { parse } from 'query-string';
import { i18n } from '@kbn/i18n';
import { CoreStart, ChromeBreadcrumb, ScopedHistory } from 'src/core/public';
+import { RedirectAppLinks } from '../../../kibana_react/public';
import { ISavedObjectsManagementServiceRegistry } from '../services';
import { SavedObjectEdition } from './object_view';
@@ -50,17 +51,20 @@ const SavedObjectsEditionPage = ({
}, [setBreadcrumbs, service]);
return (
-
+
+
+
);
};
diff --git a/src/plugins/share/public/components/__snapshots__/share_context_menu.test.tsx.snap b/src/plugins/share/public/components/__snapshots__/share_context_menu.test.tsx.snap
index fc3fa3e72b9c0f..95d3026f66d372 100644
--- a/src/plugins/share/public/components/__snapshots__/share_context_menu.test.tsx.snap
+++ b/src/plugins/share/public/components/__snapshots__/share_context_menu.test.tsx.snap
@@ -55,6 +55,7 @@ exports[`shareContextMenuExtensions should sort ascending on sort order first an
},
]
}
+ size="m"
/>
`;
@@ -78,6 +79,7 @@ exports[`should only render permalink panel when there are no other panels 1`] =
},
]
}
+ size="m"
/>
`;
@@ -130,6 +132,7 @@ exports[`should render context menu panel when there are more than one panel 1`]
},
]
}
+ size="m"
/>
`;
diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json
index 50a08d96de9514..7bac6a809eca3d 100644
--- a/src/plugins/telemetry/schema/oss_plugins.json
+++ b/src/plugins/telemetry/schema/oss_plugins.json
@@ -4268,9 +4268,6 @@
"query:queryString:options": {
"type": "keyword"
},
- "pageNavigation": {
- "type": "keyword"
- },
"metrics:max_buckets": {
"type": "long"
},
@@ -5247,6 +5244,36 @@
}
}
},
+ "vis_type_table": {
+ "properties": {
+ "total": {
+ "type": "long"
+ },
+ "total_split": {
+ "type": "long"
+ },
+ "split_columns": {
+ "properties": {
+ "total": {
+ "type": "long"
+ },
+ "enabled": {
+ "type": "long"
+ }
+ }
+ },
+ "split_rows": {
+ "properties": {
+ "total": {
+ "type": "long"
+ },
+ "enabled": {
+ "type": "long"
+ }
+ }
+ }
+ }
+ },
"vis_type_vega": {
"properties": {
"vega_lib_specs_total": {
diff --git a/src/plugins/tile_map/public/components/tile_map_options.tsx b/src/plugins/tile_map/public/components/tile_map_options.tsx
index c30d314d166bb9..d6155dbfec8773 100644
--- a/src/plugins/tile_map/public/components/tile_map_options.tsx
+++ b/src/plugins/tile_map/public/components/tile_map_options.tsx
@@ -10,8 +10,8 @@ import React, { useEffect } from 'react';
import { EuiPanel, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import {
- VisOptionsProps,
BasicOptions,
SelectOption,
SwitchOption,
@@ -21,7 +21,7 @@ import { WmsOptions } from '../../../maps_legacy/public';
import { TileMapVisParams } from '../types';
import { MapTypes } from '../utils/map_types';
-export type TileMapOptionsProps = VisOptionsProps;
+export type TileMapOptionsProps = VisEditorOptionsProps;
function TileMapOptions(props: TileMapOptionsProps) {
const { stateParams, setValue, vis } = props;
diff --git a/src/plugins/tile_map/public/tile_map_type.ts b/src/plugins/tile_map/public/tile_map_type.ts
index 7356da0cf8bcb2..ceb9c138f043c6 100644
--- a/src/plugins/tile_map/public/tile_map_type.ts
+++ b/src/plugins/tile_map/public/tile_map_type.ts
@@ -7,7 +7,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
+import { VisTypeDefinition } from 'src/plugins/visualizations/public';
import { truncatedColorSchemas } from '../../charts/public';
// @ts-expect-error
@@ -21,7 +21,7 @@ import { MapTypes } from './utils/map_types';
export function createTileMapTypeDefinition(
dependencies: TileMapVisualizationDependencies
-): BaseVisTypeOptions {
+): VisTypeDefinition {
const { uiSettings, getServiceSettings } = dependencies;
return {
@@ -147,5 +147,6 @@ export function createTileMapTypeDefinition(
}
return vis;
},
+ requiresSearch: true,
};
}
diff --git a/src/plugins/tile_map/public/to_ast.ts b/src/plugins/tile_map/public/to_ast.ts
index f5a964d2574311..8130b7df25276c 100644
--- a/src/plugins/tile_map/public/to_ast.ts
+++ b/src/plugins/tile_map/public/to_ast.ts
@@ -11,11 +11,11 @@ import {
IndexPatternLoadExpressionFunctionDefinition,
} from '../../data/public';
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
-import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public';
+import { getVisSchemas, VisToExpressionAst } from '../../visualizations/public';
import { TileMapExpressionFunctionDefinition } from './tile_map_fn';
import { TileMapVisConfig, TileMapVisParams } from './types';
-export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => {
+export const toExpressionAst: VisToExpressionAst = (vis, params) => {
const esaggs = buildExpressionFunction('esaggs', {
index: buildExpression([
buildExpressionFunction('indexPatternLoad', {
diff --git a/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts b/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts
index 131edb46015466..fb41d610d21751 100644
--- a/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts
+++ b/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts
@@ -50,7 +50,6 @@ describe('DefaultEditorGroup helpers', () => {
min: 0,
max: 3,
aggFilter: [],
- editor: false,
params: [],
defaults: null,
mustBeFirst: true,
@@ -62,7 +61,6 @@ describe('DefaultEditorGroup helpers', () => {
min: 2,
max: 3,
aggFilter: [],
- editor: false,
params: [],
defaults: null,
},
diff --git a/src/plugins/vis_default_editor/public/components/options/basic_options.tsx b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx
index 4edfd9982ffbdc..85a94e1228a3e4 100644
--- a/src/plugins/vis_default_editor/public/components/options/basic_options.tsx
+++ b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx
@@ -10,7 +10,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
-import { VisOptionsProps } from '../../vis_options_props';
+import { VisEditorOptionsProps } from '../../../../visualizations/public';
import { SwitchOption } from './switch';
import { SelectOption } from './select';
@@ -23,7 +23,7 @@ function BasicOptions({
stateParams,
setValue,
vis,
-}: VisOptionsProps) {
+}: VisEditorOptionsProps) {
return (
<>
(
interface ColorSchemaOptionsProps extends ColorSchemaParams {
disabled?: boolean;
colorSchemas: ColorSchema[];
- uiState: VisOptionsProps['uiState'];
+ uiState: VisEditorOptionsProps['uiState'];
setValue: SetColorSchemaOptionsValue;
showHelpText?: boolean;
}
diff --git a/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts b/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts
index fbec23fd199f51..be0e4dd9a8c207 100644
--- a/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts
+++ b/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts
@@ -9,13 +9,12 @@
import { useCallback, useState } from 'react';
import { i18n } from '@kbn/i18n';
-import { Vis } from '../../../../visualizations/public';
+import { Vis, VisEditorOptionsProps } from '../../../../visualizations/public';
-import { VisOptionsProps } from '../../vis_options_props';
import { DefaultEditorDataTab, DefaultEditorDataTabProps } from './data_tab';
export interface OptionTab {
- editor: React.ComponentType;
+ editor: React.ComponentType;
name: string;
title: string;
isSelected?: boolean;
diff --git a/src/plugins/vis_default_editor/public/index.ts b/src/plugins/vis_default_editor/public/index.ts
index a4c94dd664968f..9d315f1a94ce7a 100644
--- a/src/plugins/vis_default_editor/public/index.ts
+++ b/src/plugins/vis_default_editor/public/index.ts
@@ -16,7 +16,6 @@ export { PalettePicker } from './components/controls/palette_picker';
export * from './components/options';
export { RangesParamEditor, RangeValues } from './components/controls/ranges';
export * from './editor_size';
-export * from './vis_options_props';
export * from './utils';
export const plugin = (context: PluginInitializerContext) => {
diff --git a/src/plugins/vis_default_editor/public/vis_options_props.tsx b/src/plugins/vis_default_editor/public/vis_options_props.tsx
deleted file mode 100644
index 6180686b6996d3..00000000000000
--- a/src/plugins/vis_default_editor/public/vis_options_props.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * and the Server Side Public License, v 1; you may not use this file except in
- * compliance with, at your election, the Elastic License or the Server Side
- * Public License, v 1.
- */
-
-import { Vis, PersistedState } from 'src/plugins/visualizations/public';
-import { IAggConfigs } from 'src/plugins/data/public';
-
-export interface VisOptionsProps {
- aggs: IAggConfigs;
- hasHistogramAgg: boolean;
- isTabSelected: boolean;
- stateParams: VisParamType;
- vis: Vis;
- uiState: PersistedState;
- setValue(paramName: T, value: VisParamType[T]): void;
- setValidity(isValid: boolean): void;
- setTouched(isTouched: boolean): void;
-}
diff --git a/src/plugins/vis_type_markdown/public/markdown_options.test.tsx b/src/plugins/vis_type_markdown/public/markdown_options.test.tsx
index cf27118ac71e4a..9d464cf9e20633 100644
--- a/src/plugins/vis_type_markdown/public/markdown_options.test.tsx
+++ b/src/plugins/vis_type_markdown/public/markdown_options.test.tsx
@@ -9,7 +9,7 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { MarkdownVisParams } from './types';
import { MarkdownOptions } from './markdown_options';
@@ -21,7 +21,7 @@ describe('MarkdownOptions', () => {
openLinksInNewTab: false,
},
setValue: jest.fn(),
- } as unknown) as VisOptionsProps;
+ } as unknown) as VisEditorOptionsProps;
it('should match snapshot', () => {
const comp = shallow();
diff --git a/src/plugins/vis_type_markdown/public/markdown_options.tsx b/src/plugins/vis_type_markdown/public/markdown_options.tsx
index 72e4aaf8758b2d..79d850b8c1fd86 100644
--- a/src/plugins/vis_type_markdown/public/markdown_options.tsx
+++ b/src/plugins/vis_type_markdown/public/markdown_options.tsx
@@ -18,10 +18,10 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { MarkdownVisParams } from './types';
-function MarkdownOptions({ stateParams, setValue }: VisOptionsProps) {
+function MarkdownOptions({ stateParams, setValue }: VisEditorOptionsProps) {
const onMarkdownUpdate = useCallback(
({ target: { value } }: React.ChangeEvent) => setValue('markdown', value),
[setValue]
diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts
index e698316dc8e702..adfdb811cc28ce 100644
--- a/src/plugins/vis_type_markdown/public/markdown_vis.ts
+++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts
@@ -11,10 +11,11 @@ import { i18n } from '@kbn/i18n';
import { MarkdownOptions } from './markdown_options';
import { SettingsOptions } from './settings_options_lazy';
import { DefaultEditorSize } from '../../vis_default_editor/public';
-import { VisGroups } from '../../visualizations/public';
+import { VisGroups, VisTypeDefinition } from '../../visualizations/public';
import { toExpressionAst } from './to_ast';
+import { MarkdownVisParams } from './types';
-export const markdownVisDefinition = {
+export const markdownVisDefinition: VisTypeDefinition = {
name: 'markdown',
title: 'Markdown',
isAccessible: true,
@@ -58,7 +59,5 @@ export const markdownVisDefinition = {
showTimePicker: false,
showFilterBar: false,
},
- requestHandler: 'none',
- responseHandler: 'none',
inspectorAdapters: {},
};
diff --git a/src/plugins/vis_type_markdown/public/settings_options.tsx b/src/plugins/vis_type_markdown/public/settings_options.tsx
index 4ebcc88a8c04f9..c18f1305d778ac 100644
--- a/src/plugins/vis_type_markdown/public/settings_options.tsx
+++ b/src/plugins/vis_type_markdown/public/settings_options.tsx
@@ -10,10 +10,11 @@ import React from 'react';
import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { VisOptionsProps, SwitchOption, RangeOption } from '../../vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
+import { SwitchOption, RangeOption } from '../../vis_default_editor/public';
import { MarkdownVisParams } from './types';
-function SettingsOptions({ stateParams, setValue }: VisOptionsProps) {
+function SettingsOptions({ stateParams, setValue }: VisEditorOptionsProps) {
return (
{
+export const toExpressionAst: VisToExpressionAst = (vis) => {
const { markdown, fontSize, openLinksInNewTab } = vis.params;
const markdownVis = buildExpressionFunction(
diff --git a/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap b/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap
index fd8f3a712d8ae5..a212cec2d88274 100644
--- a/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap
+++ b/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap
@@ -5,9 +5,6 @@ Object {
"as": "metric_vis",
"type": "render",
"value": Object {
- "params": Object {
- "listenOnChange": true,
- },
"visConfig": Object {
"dimensions": Object {
"metrics": undefined,
diff --git a/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap
index a89f41737d7bcb..46e86c4c25de1f 100644
--- a/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap
+++ b/src/plugins/vis_type_metric/public/__snapshots__/to_ast.test.ts.snap
@@ -37,6 +37,9 @@ Object {
"percentageMode": Array [
true,
],
+ "showLabels": Array [
+ false,
+ ],
},
"function": "metricVis",
"type": "function",
@@ -79,7 +82,11 @@ Object {
"type": "function",
},
Object {
- "arguments": Object {},
+ "arguments": Object {
+ "showLabels": Array [
+ false,
+ ],
+ },
"function": "metricVis",
"type": "function",
},
diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx
index c8448ae68c074c..069690b58e2a00 100644
--- a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx
+++ b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx
@@ -18,10 +18,10 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import {
ColorRanges,
SetColorRangeValue,
- VisOptionsProps,
SwitchOption,
SetColorSchemaOptionsValue,
ColorSchemaOptions,
@@ -37,7 +37,7 @@ function MetricVisOptions({
setTouched,
vis,
uiState,
-}: VisOptionsProps) {
+}: VisEditorOptionsProps) {
const setMetricValue: (
paramName: T,
value: MetricVisParam[T]
diff --git a/src/plugins/vis_type_metric/public/metric_vis_fn.ts b/src/plugins/vis_type_metric/public/metric_vis_fn.ts
index 892523493fb781..083d0413161b26 100644
--- a/src/plugins/vis_type_metric/public/metric_vis_fn.ts
+++ b/src/plugins/vis_type_metric/public/metric_vis_fn.ts
@@ -39,7 +39,6 @@ export interface MetricVisRenderValue {
visType: typeof visType;
visData: Input;
visConfig: Pick;
- params: any;
}
export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition<
@@ -194,9 +193,6 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({
},
dimensions,
},
- params: {
- listenOnChange: true,
- },
},
};
},
diff --git a/src/plugins/vis_type_metric/public/metric_vis_type.ts b/src/plugins/vis_type_metric/public/metric_vis_type.ts
index cd4357574950db..e81d8f071c4f09 100644
--- a/src/plugins/vis_type_metric/public/metric_vis_type.ts
+++ b/src/plugins/vis_type_metric/public/metric_vis_type.ts
@@ -9,11 +9,12 @@
import { i18n } from '@kbn/i18n';
import { MetricVisOptions } from './components/metric_vis_options';
import { ColorSchemas, colorSchemas, ColorMode } from '../../charts/public';
-import { BaseVisTypeOptions } from '../../visualizations/public';
+import { VisTypeDefinition } from '../../visualizations/public';
import { AggGroupNames } from '../../data/public';
import { toExpressionAst } from './to_ast';
+import { VisParams } from './types';
-export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({
+export const createMetricVisTypeDefinition = (): VisTypeDefinition => ({
name: 'metric',
title: i18n.translate('visTypeMetric.metricTitle', { defaultMessage: 'Metric' }),
icon: 'visMetric',
@@ -110,4 +111,5 @@ export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({
},
],
},
+ requiresSearch: true,
});
diff --git a/src/plugins/vis_type_metric/public/to_ast.test.ts b/src/plugins/vis_type_metric/public/to_ast.test.ts
index fb90b27391c839..871b2ca0dc3795 100644
--- a/src/plugins/vis_type_metric/public/to_ast.test.ts
+++ b/src/plugins/vis_type_metric/public/to_ast.test.ts
@@ -6,14 +6,17 @@
* Public License, v 1.
*/
+import { TimefilterContract } from 'src/plugins/data/public';
+import { Vis } from 'src/plugins/visualizations/public';
+
import { toExpressionAst } from './to_ast';
-import { Vis } from '../../visualizations/public';
+import { VisParams } from './types';
describe('metric vis toExpressionAst function', () => {
- let vis: Vis;
+ let vis: Vis;
beforeEach(() => {
- vis = {
+ vis = ({
isHierarchical: () => false,
type: {},
params: {
@@ -26,18 +29,22 @@ describe('metric vis toExpressionAst function', () => {
aggs: [],
} as any,
},
- } as any;
+ } as unknown) as Vis;
});
it('without params', () => {
- vis.params = { metric: {} };
- const actual = toExpressionAst(vis, {});
+ vis.params = { metric: {} } as VisParams;
+ const actual = toExpressionAst(vis, {
+ timefilter: {} as TimefilterContract,
+ });
expect(actual).toMatchSnapshot();
});
it('with percentage mode should have percentage format', () => {
- vis.params = { metric: { percentageMode: true } };
- const actual = toExpressionAst(vis, {});
+ vis.params = { metric: { percentageMode: true } } as VisParams;
+ const actual = toExpressionAst(vis, {
+ timefilter: {} as TimefilterContract,
+ });
expect(actual).toMatchSnapshot();
});
});
diff --git a/src/plugins/vis_type_metric/public/to_ast.ts b/src/plugins/vis_type_metric/public/to_ast.ts
index 3b95b0c841016f..3addce7610f2e9 100644
--- a/src/plugins/vis_type_metric/public/to_ast.ts
+++ b/src/plugins/vis_type_metric/public/to_ast.ts
@@ -7,13 +7,14 @@
*/
import { get } from 'lodash';
-import { getVisSchemas, SchemaConfig, Vis } from '../../visualizations/public';
+import { getVisSchemas, SchemaConfig, VisToExpressionAst } from '../../visualizations/public';
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
import { MetricVisExpressionFunctionDefinition } from './metric_vis_fn';
import {
EsaggsExpressionFunctionDefinition,
IndexPatternLoadExpressionFunctionDefinition,
} from '../../data/public';
+import { VisParams } from './types';
const prepareDimension = (params: SchemaConfig) => {
const visdimension = buildExpressionFunction('visdimension', { accessor: params.accessor });
@@ -26,7 +27,7 @@ const prepareDimension = (params: SchemaConfig) => {
return buildExpression([visdimension]);
};
-export const toExpressionAst = (vis: Vis, params: any) => {
+export const toExpressionAst: VisToExpressionAst = (vis, params) => {
const esaggs = buildExpressionFunction('esaggs', {
index: buildExpression([
buildExpressionFunction('indexPatternLoad', {
@@ -34,7 +35,7 @@ export const toExpressionAst = (vis: Vis, params: any) => {
}),
]),
metricsAtAllLevels: vis.isHierarchical(),
- partialRows: vis.params.showPartialRows || false,
+ partialRows: false,
aggs: vis.data.aggs!.aggs.map((agg) => buildExpression(agg.toExpressionAst())),
});
@@ -65,7 +66,7 @@ export const toExpressionAst = (vis: Vis, params: any) => {
colorMode: metricColorMode,
useRanges,
invertColors,
- showLabels: labels && labels.show,
+ showLabels: labels?.show ?? false,
});
if (style) {
diff --git a/src/plugins/vis_type_table/common/index.ts b/src/plugins/vis_type_table/common/index.ts
new file mode 100644
index 00000000000000..cc54db82d37e7a
--- /dev/null
+++ b/src/plugins/vis_type_table/common/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+export * from './types';
diff --git a/src/plugins/vis_type_table/common/types.ts b/src/plugins/vis_type_table/common/types.ts
new file mode 100644
index 00000000000000..3380e730770c36
--- /dev/null
+++ b/src/plugins/vis_type_table/common/types.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+export const VIS_TYPE_TABLE = 'table';
+
+export enum AggTypes {
+ SUM = 'sum',
+ AVG = 'avg',
+ MIN = 'min',
+ MAX = 'max',
+ COUNT = 'count',
+}
+
+export interface TableVisParams {
+ perPage: number | '';
+ showPartialRows: boolean;
+ showMetricsAtAllLevels: boolean;
+ showToolbar: boolean;
+ showTotal: boolean;
+ totalFunc: AggTypes;
+ percentageCol: string;
+ row?: boolean;
+}
diff --git a/src/plugins/vis_type_table/jest.config.js b/src/plugins/vis_type_table/jest.config.js
index 4e5ddbcf8d7c54..3a7906f6ec543a 100644
--- a/src/plugins/vis_type_table/jest.config.js
+++ b/src/plugins/vis_type_table/jest.config.js
@@ -11,4 +11,5 @@ module.exports = {
rootDir: '../../..',
roots: ['/src/plugins/vis_type_table'],
testRunner: 'jasmine2',
+ collectCoverageFrom: ['/src/plugins/vis_type_table/**/*.{js,ts,tsx}'],
};
diff --git a/src/plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx
index eb76659a601d62..163dae7ddc1c66 100644
--- a/src/plugins/vis_type_table/public/components/table_vis_options.tsx
+++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx
@@ -12,14 +12,10 @@ import { EuiIconTip, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { search } from '../../../data/public';
-import {
- SwitchOption,
- SelectOption,
- NumberInputOption,
- VisOptionsProps,
-} from '../../../vis_default_editor/public';
-import { TableVisParams } from '../types';
+import { SwitchOption, SelectOption, NumberInputOption } from '../../../vis_default_editor/public';
+import { TableVisParams } from '../../common';
import { totalAggregations } from './utils';
const { tabifyGetColumns } = search;
@@ -29,7 +25,7 @@ function TableOptions({
stateParams,
setValidity,
setValue,
-}: VisOptionsProps) {
+}: VisEditorOptionsProps) {
const percentageColumns = useMemo(
() => [
{
diff --git a/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx
index fb0044a986f5e9..2f7369996634a6 100644
--- a/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx
+++ b/src/plugins/vis_type_table/public/components/table_vis_options_lazy.tsx
@@ -8,12 +8,13 @@
import React, { lazy, Suspense } from 'react';
import { EuiLoadingSpinner } from '@elastic/eui';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
-import { TableVisParams } from '../types';
+
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
+import { TableVisParams } from '../../common';
const TableOptionsComponent = lazy(() => import('./table_vis_options'));
-export const TableOptions = (props: VisOptionsProps) => (
+export const TableOptions = (props: VisEditorOptionsProps) => (
}>
diff --git a/src/plugins/vis_type_table/public/components/utils.ts b/src/plugins/vis_type_table/public/components/utils.ts
index f11d7bc4b7f332..8f30788c764689 100644
--- a/src/plugins/vis_type_table/public/components/utils.ts
+++ b/src/plugins/vis_type_table/public/components/utils.ts
@@ -7,7 +7,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { AggTypes } from '../types';
+import { AggTypes } from '../../common';
const totalAggregations = [
{
diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts
index cec16eefb360c4..db0b92154d2dd7 100644
--- a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts
+++ b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_fn.ts
@@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n';
import { ExpressionFunctionDefinition, Datatable, Render } from 'src/plugins/expressions/public';
import { tableVisLegacyResponseHandler, TableContext } from './table_vis_legacy_response_handler';
import { TableVisConfig } from '../types';
+import { VIS_TYPE_TABLE } from '../../common';
export type Input = Datatable;
@@ -19,7 +20,7 @@ interface Arguments {
export interface TableVisRenderValue {
visData: TableContext;
- visType: 'table';
+ visType: typeof VIS_TYPE_TABLE;
visConfig: TableVisConfig;
}
@@ -53,7 +54,7 @@ export const createTableVisLegacyFn = (): TableExpressionFunctionDefinition => (
as: 'table_vis',
value: {
visData: convertedData,
- visType: 'table',
+ visType: VIS_TYPE_TABLE,
visConfig,
},
};
diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts
index a1ceee8c741d4b..53ad67ab4dd94e 100644
--- a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts
+++ b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_type.ts
@@ -8,15 +8,15 @@
import { i18n } from '@kbn/i18n';
import { AggGroupNames } from '../../../data/public';
-import { BaseVisTypeOptions } from '../../../visualizations/public';
+import { VisTypeDefinition } from '../../../visualizations/public';
import { TableOptions } from '../components/table_vis_options_lazy';
import { VIS_EVENT_TO_TRIGGER } from '../../../visualizations/public';
+import { TableVisParams, VIS_TYPE_TABLE } from '../../common';
import { toExpressionAst } from '../to_ast';
-import { TableVisParams } from '../types';
-export const tableVisLegacyTypeDefinition: BaseVisTypeOptions = {
- name: 'table',
+export const tableVisLegacyTypeDefinition: VisTypeDefinition = {
+ name: VIS_TYPE_TABLE,
title: i18n.translate('visTypeTable.tableVisTitle', {
defaultMessage: 'Data table',
}),
@@ -81,4 +81,5 @@ export const tableVisLegacyTypeDefinition: BaseVisTypeOptions =
},
toExpressionAst,
hierarchicalData: (vis) => vis.params.showPartialRows || vis.params.showMetricsAtAllLevels,
+ requiresSearch: true,
};
diff --git a/src/plugins/vis_type_table/public/table_vis_fn.ts b/src/plugins/vis_type_table/public/table_vis_fn.ts
index a45f1e828fc47a..99fee424b8bea9 100644
--- a/src/plugins/vis_type_table/public/table_vis_fn.ts
+++ b/src/plugins/vis_type_table/public/table_vis_fn.ts
@@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n';
import { tableVisResponseHandler, TableContext } from './table_vis_response_handler';
import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public';
import { TableVisConfig } from './types';
+import { VIS_TYPE_TABLE } from '../common';
export type Input = Datatable;
@@ -19,7 +20,7 @@ interface Arguments {
export interface TableVisRenderValue {
visData: TableContext;
- visType: 'table';
+ visType: typeof VIS_TYPE_TABLE;
visConfig: TableVisConfig;
}
@@ -56,7 +57,7 @@ export const createTableVisFn = (): TableExpressionFunctionDefinition => ({
as: 'table_vis',
value: {
visData: convertedData,
- visType: 'table',
+ visType: VIS_TYPE_TABLE,
visConfig,
},
};
diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts
index 8cd45b54c6ced2..92148a5f085b0c 100644
--- a/src/plugins/vis_type_table/public/table_vis_type.ts
+++ b/src/plugins/vis_type_table/public/table_vis_type.ts
@@ -7,16 +7,15 @@
*/
import { i18n } from '@kbn/i18n';
-import { AggGroupNames } from '../../data/public';
-import { BaseVisTypeOptions } from '../../visualizations/public';
+import { AggGroupNames } from '../../data/public';
+import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '../../visualizations/public';
+import { TableVisParams, VIS_TYPE_TABLE } from '../common';
import { TableOptions } from './components/table_vis_options_lazy';
-import { VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public';
import { toExpressionAst } from './to_ast';
-import { TableVisParams } from './types';
-export const tableVisTypeDefinition: BaseVisTypeOptions = {
- name: 'table',
+export const tableVisTypeDefinition: VisTypeDefinition = {
+ name: VIS_TYPE_TABLE,
title: i18n.translate('visTypeTable.tableVisTitle', {
defaultMessage: 'Data table',
}),
@@ -78,4 +77,5 @@ export const tableVisTypeDefinition: BaseVisTypeOptions = {
},
toExpressionAst,
hierarchicalData: (vis) => vis.params.showPartialRows || vis.params.showMetricsAtAllLevels,
+ requiresSearch: true,
};
diff --git a/src/plugins/vis_type_table/public/to_ast.test.ts b/src/plugins/vis_type_table/public/to_ast.test.ts
index 1ca62475b7af0d..f0aed7199a2f2d 100644
--- a/src/plugins/vis_type_table/public/to_ast.test.ts
+++ b/src/plugins/vis_type_table/public/to_ast.test.ts
@@ -8,7 +8,7 @@
import { Vis } from 'src/plugins/visualizations/public';
import { toExpressionAst } from './to_ast';
-import { AggTypes, TableVisParams } from './types';
+import { AggTypes, TableVisParams } from '../common';
const mockSchemas = {
metric: [{ accessor: 1, format: { id: 'number' }, params: {}, label: 'Count', aggType: 'count' }],
diff --git a/src/plugins/vis_type_table/public/to_ast.ts b/src/plugins/vis_type_table/public/to_ast.ts
index 9d9f23d31d802a..ba79d82947d06a 100644
--- a/src/plugins/vis_type_table/public/to_ast.ts
+++ b/src/plugins/vis_type_table/public/to_ast.ts
@@ -11,9 +11,10 @@ import {
IndexPatternLoadExpressionFunctionDefinition,
} from '../../data/public';
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
-import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public';
+import { getVisSchemas, VisToExpressionAst } from '../../visualizations/public';
+import { TableVisParams } from '../common';
import { TableExpressionFunctionDefinition } from './table_vis_fn';
-import { TableVisConfig, TableVisParams } from './types';
+import { TableVisConfig } from './types';
const buildTableVisConfig = (
schemas: ReturnType,
@@ -40,7 +41,7 @@ const buildTableVisConfig = (
return visConfig;
};
-export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => {
+export const toExpressionAst: VisToExpressionAst = (vis, params) => {
const esaggs = buildExpressionFunction('esaggs', {
index: buildExpression([
buildExpressionFunction('indexPatternLoad', {
diff --git a/src/plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts
index 75d48f4f53ac7e..03cf8bb3395d6d 100644
--- a/src/plugins/vis_type_table/public/types.ts
+++ b/src/plugins/vis_type_table/public/types.ts
@@ -8,14 +8,7 @@
import { IFieldFormat } from 'src/plugins/data/public';
import { SchemaConfig } from 'src/plugins/visualizations/public';
-
-export enum AggTypes {
- SUM = 'sum',
- AVG = 'avg',
- MIN = 'min',
- MAX = 'max',
- COUNT = 'count',
-}
+import { TableVisParams } from '../common';
export interface Dimensions {
buckets: SchemaConfig[];
@@ -44,16 +37,6 @@ export interface TableVisUseUiStateProps {
setColumnsWidth: (column: ColumnWidthData) => void;
}
-export interface TableVisParams {
- perPage: number | '';
- showPartialRows: boolean;
- showMetricsAtAllLevels: boolean;
- showToolbar: boolean;
- showTotal: boolean;
- totalFunc: AggTypes;
- percentageCol: string;
-}
-
export interface TableVisConfig extends TableVisParams {
title: string;
dimensions: Dimensions;
diff --git a/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts b/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts
index 5398aa908f6eb8..3a733e7a9a4dcf 100644
--- a/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts
+++ b/src/plugins/vis_type_table/public/utils/use/use_formatted_columns.ts
@@ -9,8 +9,9 @@
import { useMemo } from 'react';
import { chain, findIndex } from 'lodash';
+import { AggTypes } from '../../../common';
import { Table } from '../../table_vis_response_handler';
-import { FormattedColumn, TableVisConfig, AggTypes } from '../../types';
+import { FormattedColumn, TableVisConfig } from '../../types';
import { getFormatService } from '../../services';
import { addPercentageColumn } from '../add_percentage_column';
diff --git a/src/plugins/vis_type_table/public/utils/use/use_pagination.ts b/src/plugins/vis_type_table/public/utils/use/use_pagination.ts
index 1573a3c6b7b880..7e55e63f9249c2 100644
--- a/src/plugins/vis_type_table/public/utils/use/use_pagination.ts
+++ b/src/plugins/vis_type_table/public/utils/use/use_pagination.ts
@@ -7,7 +7,7 @@
*/
import { useCallback, useEffect, useMemo, useState } from 'react';
-import { TableVisParams } from '../../types';
+import { TableVisParams } from '../../../common';
export const usePagination = (visParams: TableVisParams, rowCount: number) => {
const [pagination, setPagination] = useState({
diff --git a/src/plugins/vis_type_table/server/index.ts b/src/plugins/vis_type_table/server/index.ts
index 75068c646f501c..39618d687168e6 100644
--- a/src/plugins/vis_type_table/server/index.ts
+++ b/src/plugins/vis_type_table/server/index.ts
@@ -6,9 +6,11 @@
* Public License, v 1.
*/
-import { PluginConfigDescriptor } from 'kibana/server';
+import { CoreSetup, PluginConfigDescriptor } from 'kibana/server';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { configSchema, ConfigSchema } from '../config';
+import { registerVisTypeTableUsageCollector } from './usage_collector';
export const config: PluginConfigDescriptor = {
exposeToBrowser: {
@@ -21,6 +23,10 @@ export const config: PluginConfigDescriptor = {
};
export const plugin = () => ({
- setup() {},
+ setup(core: CoreSetup, plugins: { usageCollection?: UsageCollectionSetup }) {
+ if (plugins.usageCollection) {
+ registerVisTypeTableUsageCollector(plugins.usageCollection);
+ }
+ },
start() {},
});
diff --git a/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts b/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts
new file mode 100644
index 00000000000000..55daa5c64349a0
--- /dev/null
+++ b/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts
@@ -0,0 +1,67 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+import { SavedObjectsClientContract } from 'kibana/server';
+import { getStats } from './get_stats';
+
+const mockVisualizations = {
+ saved_objects: [
+ {
+ attributes: {
+ visState:
+ '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "bucket" }, { "schema": "split", "enabled": true }], "params": { "row": true }}',
+ },
+ },
+ {
+ attributes: {
+ visState:
+ '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "bucket" }, { "schema": "split", "enabled": false }], "params": { "row": true }}',
+ },
+ },
+ {
+ attributes: {
+ visState:
+ '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "split", "enabled": true }], "params": { "row": false }}',
+ },
+ },
+ {
+ attributes: {
+ visState: '{"type": "table","aggs": [{ "schema": "metric" }, { "schema": "bucket" }]}',
+ },
+ },
+ {
+ attributes: { visState: '{"type": "histogram"}' },
+ },
+ ],
+};
+
+describe('vis_type_table getStats', () => {
+ const mockSoClient = ({
+ find: jest.fn().mockResolvedValue(mockVisualizations),
+ } as unknown) as SavedObjectsClientContract;
+
+ test('Returns stats from saved objects for table vis only', async () => {
+ const result = await getStats(mockSoClient);
+ expect(mockSoClient.find).toHaveBeenCalledWith({
+ type: 'visualization',
+ perPage: 10000,
+ });
+ expect(result).toEqual({
+ total: 4,
+ total_split: 3,
+ split_columns: {
+ total: 1,
+ enabled: 1,
+ },
+ split_rows: {
+ total: 2,
+ enabled: 1,
+ },
+ });
+ });
+});
diff --git a/src/plugins/vis_type_table/server/usage_collector/get_stats.ts b/src/plugins/vis_type_table/server/usage_collector/get_stats.ts
new file mode 100644
index 00000000000000..bd3e1d2f089e25
--- /dev/null
+++ b/src/plugins/vis_type_table/server/usage_collector/get_stats.ts
@@ -0,0 +1,85 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+import { ISavedObjectsRepository, SavedObjectsClientContract } from 'kibana/server';
+import {
+ SavedVisState,
+ VisualizationSavedObjectAttributes,
+} from 'src/plugins/visualizations/common';
+import { TableVisParams, VIS_TYPE_TABLE } from '../../common';
+
+export interface VisTypeTableUsage {
+ /**
+ * Total number of table type visualizations
+ */
+ total: number;
+ /**
+ * Total number of table visualizations, using "Split table" agg
+ */
+ total_split: number;
+ /**
+ * Split table by columns stats
+ */
+ split_columns: {
+ total: number;
+ enabled: number;
+ };
+ /**
+ * Split table by rows stats
+ */
+ split_rows: {
+ total: number;
+ enabled: number;
+ };
+}
+
+/*
+ * Parse the response data into telemetry payload
+ */
+export async function getStats(
+ soClient: SavedObjectsClientContract | ISavedObjectsRepository
+): Promise {
+ const visualizations = await soClient.find({
+ type: 'visualization',
+ perPage: 10000,
+ });
+
+ const tableVisualizations = visualizations.saved_objects
+ .map>(({ attributes }) => JSON.parse(attributes.visState))
+ .filter(({ type }) => type === VIS_TYPE_TABLE);
+
+ const defaultStats = {
+ total: tableVisualizations.length,
+ total_split: 0,
+ split_columns: {
+ total: 0,
+ enabled: 0,
+ },
+ split_rows: {
+ total: 0,
+ enabled: 0,
+ },
+ };
+
+ return tableVisualizations.reduce((acc, { aggs, params }) => {
+ const hasSplitAgg = aggs.find((agg) => agg.schema === 'split');
+
+ if (hasSplitAgg) {
+ acc.total_split += 1;
+
+ const isSplitRow = params.row;
+ const isSplitEnabled = hasSplitAgg.enabled;
+
+ const container = isSplitRow ? acc.split_rows : acc.split_columns;
+ container.total += 1;
+ container.enabled = isSplitEnabled ? container.enabled + 1 : container.enabled;
+ }
+
+ return acc;
+ }, defaultStats);
+}
diff --git a/src/plugins/vis_type_table/server/usage_collector/index.ts b/src/plugins/vis_type_table/server/usage_collector/index.ts
new file mode 100644
index 00000000000000..090ed3077b27c4
--- /dev/null
+++ b/src/plugins/vis_type_table/server/usage_collector/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+export { registerVisTypeTableUsageCollector } from './register_usage_collector';
diff --git a/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts
new file mode 100644
index 00000000000000..cbf39a4d937a77
--- /dev/null
+++ b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.test.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+jest.mock('./get_stats', () => ({
+ getStats: jest.fn().mockResolvedValue({ somestat: 1 }),
+}));
+
+import { createUsageCollectionSetupMock } from 'src/plugins/usage_collection/server/usage_collection.mock';
+import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks';
+
+import { registerVisTypeTableUsageCollector } from './register_usage_collector';
+import { getStats } from './get_stats';
+
+describe('registerVisTypeTableUsageCollector', () => {
+ it('Usage collector configs fit the shape', () => {
+ const mockCollectorSet = createUsageCollectionSetupMock();
+ registerVisTypeTableUsageCollector(mockCollectorSet);
+ expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1);
+ expect(mockCollectorSet.registerCollector).toBeCalledTimes(1);
+ expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({
+ type: 'vis_type_table',
+ isReady: expect.any(Function),
+ fetch: expect.any(Function),
+ schema: {
+ total: { type: 'long' },
+ total_split: { type: 'long' },
+ split_columns: {
+ total: { type: 'long' },
+ enabled: { type: 'long' },
+ },
+ split_rows: {
+ total: { type: 'long' },
+ enabled: { type: 'long' },
+ },
+ },
+ });
+ const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0];
+ expect(usageCollectorConfig.isReady()).toBe(true);
+ });
+
+ it('Usage collector config.fetch calls getStats', async () => {
+ const mockCollectorSet = createUsageCollectionSetupMock();
+ registerVisTypeTableUsageCollector(mockCollectorSet);
+ const usageCollector = mockCollectorSet.makeUsageCollector.mock.results[0].value;
+ const mockCollectorFetchContext = createCollectorFetchContextMock();
+ const fetchResult = await usageCollector.fetch(mockCollectorFetchContext);
+ expect(getStats).toBeCalledTimes(1);
+ expect(getStats).toBeCalledWith(mockCollectorFetchContext.soClient);
+ expect(fetchResult).toEqual({ somestat: 1 });
+ });
+});
diff --git a/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.ts b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.ts
new file mode 100644
index 00000000000000..2ac4ce22a47e49
--- /dev/null
+++ b/src/plugins/vis_type_table/server/usage_collector/register_usage_collector.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * and the Server Side Public License, v 1; you may not use this file except in
+ * compliance with, at your election, the Elastic License or the Server Side
+ * Public License, v 1.
+ */
+
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+
+import { getStats, VisTypeTableUsage } from './get_stats';
+
+export function registerVisTypeTableUsageCollector(collectorSet: UsageCollectionSetup) {
+ const collector = collectorSet.makeUsageCollector({
+ type: 'vis_type_table',
+ isReady: () => true,
+ schema: {
+ total: { type: 'long' },
+ total_split: { type: 'long' },
+ split_columns: {
+ total: { type: 'long' },
+ enabled: { type: 'long' },
+ },
+ split_rows: {
+ total: { type: 'long' },
+ enabled: { type: 'long' },
+ },
+ },
+ fetch: ({ soClient }) => getStats(soClient),
+ });
+ collectorSet.registerCollector(collector);
+}
diff --git a/src/plugins/vis_type_table/tsconfig.json b/src/plugins/vis_type_table/tsconfig.json
index bda86d06c0ff73..ccff3c349cf21c 100644
--- a/src/plugins/vis_type_table/tsconfig.json
+++ b/src/plugins/vis_type_table/tsconfig.json
@@ -8,6 +8,7 @@
"declarationMap": true
},
"include": [
+ "common/**/*",
"public/**/*",
"server/**/*",
"*.ts"
diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx
index 2bcae41fae566d..d15772b3fdb184 100644
--- a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx
+++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx
@@ -9,11 +9,12 @@
import React from 'react';
import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { VisOptionsProps, SelectOption, SwitchOption } from '../../../vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
+import { SelectOption, SwitchOption } from '../../../vis_default_editor/public';
import { ValidatedDualRange } from '../../../kibana_react/public';
import { TagCloudVisParams } from '../types';
-function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) {
+function TagCloudOptions({ stateParams, setValue, vis }: VisEditorOptionsProps) {
const handleFontSizeChange = ([minFontSize, maxFontSize]: [string | number, string | number]) => {
setValue('minFontSize', Number(minFontSize));
setValue('maxFontSize', Number(maxFontSize));
diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts
index 8159192b3491ac..ebfa8db41944c6 100644
--- a/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts
+++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_type.ts
@@ -7,6 +7,7 @@
*/
import { i18n } from '@kbn/i18n';
+import { AggGroupNames } from '../../data/public';
import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
import { TagCloudOptions } from './components/tag_cloud_options';
@@ -78,7 +79,7 @@ export const tagCloudVisTypeDefinition = {
optionsTemplate: TagCloudOptions,
schemas: [
{
- group: 'metrics',
+ group: AggGroupNames.Metrics,
name: 'metric',
title: i18n.translate('visTypeTagCloud.vis.schemas.metricTitle', {
defaultMessage: 'Tag size',
@@ -96,7 +97,7 @@ export const tagCloudVisTypeDefinition = {
defaults: [{ schema: 'metric', type: 'count' }],
},
{
- group: 'buckets',
+ group: AggGroupNames.Buckets,
name: 'segment',
title: i18n.translate('visTypeTagCloud.vis.schemas.segmentTitle', {
defaultMessage: 'Tags',
@@ -107,4 +108,5 @@ export const tagCloudVisTypeDefinition = {
},
],
},
+ requiresSearch: true,
};
diff --git a/src/plugins/vis_type_tagcloud/public/to_ast.ts b/src/plugins/vis_type_tagcloud/public/to_ast.ts
index adde82dd0dda92..1d9b24235b7ff7 100644
--- a/src/plugins/vis_type_tagcloud/public/to_ast.ts
+++ b/src/plugins/vis_type_tagcloud/public/to_ast.ts
@@ -11,7 +11,7 @@ import {
IndexPatternLoadExpressionFunctionDefinition,
} from '../../data/public';
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
-import { getVisSchemas, SchemaConfig, Vis, BuildPipelineParams } from '../../visualizations/public';
+import { getVisSchemas, SchemaConfig, VisToExpressionAst } from '../../visualizations/public';
import { TagcloudExpressionFunctionDefinition } from './tag_cloud_fn';
import { TagCloudVisParams } from './types';
@@ -26,7 +26,7 @@ const prepareDimension = (params: SchemaConfig) => {
return buildExpression([visdimension]);
};
-export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => {
+export const toExpressionAst: VisToExpressionAst = (vis, params) => {
const esaggs = buildExpressionFunction('esaggs', {
index: buildExpression([
buildExpressionFunction('indexPatternLoad', {
diff --git a/src/plugins/vis_type_timelion/public/timelion_options.tsx b/src/plugins/vis_type_timelion/public/timelion_options.tsx
index 4627a5d5c86575..e366ec98f4be00 100644
--- a/src/plugins/vis_type_timelion/public/timelion_options.tsx
+++ b/src/plugins/vis_type_timelion/public/timelion_options.tsx
@@ -9,7 +9,7 @@
import React, { useCallback } from 'react';
import { EuiPanel } from '@elastic/eui';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { KibanaContextProvider } from '../../kibana_react/public';
import { TimelionVisParams } from './timelion_vis_fn';
@@ -18,7 +18,7 @@ import { TimelionVisDependencies } from './plugin';
import './timelion_options.scss';
-export type TimelionOptionsProps = VisOptionsProps;
+export type TimelionOptionsProps = VisEditorOptionsProps;
function TimelionOptions({
services,
diff --git a/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
index 21344fc794c98c..c9780ec6bcd1cb 100644
--- a/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
+++ b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
@@ -10,7 +10,6 @@ import React, { lazy } from 'react';
import { i18n } from '@kbn/i18n';
import { DefaultEditorSize } from '../../vis_default_editor/public';
-import { getTimelionRequestHandler } from './helpers/timelion_request_handler';
import { TimelionOptionsProps } from './timelion_options';
import { TimelionVisDependencies } from './plugin';
import { toExpressionAst } from './to_ast';
@@ -22,8 +21,6 @@ const TimelionOptions = lazy(() => import('./timelion_options'));
export const TIMELION_VIS_NAME = 'timelion';
export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) {
- const timelionRequestHandler = getTimelionRequestHandler(dependencies);
-
// return the visType object, which kibana will use to display and configure new
// Vis object of this type.
return {
@@ -45,9 +42,7 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies)
),
defaultSize: DefaultEditorSize.MEDIUM,
},
- requestHandler: timelionRequestHandler,
toExpressionAst,
- responseHandler: 'none',
inspectorAdapters: {},
getSupportedTriggers: () => {
return [VIS_EVENT_TO_TRIGGER.applyFilter];
@@ -57,5 +52,6 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies)
showQueryBar: false,
showFilterBar: false,
},
+ requiresSearch: true,
};
}
diff --git a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx
index 7afc01adec385b..0a54a74b6b68c8 100644
--- a/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx
+++ b/src/plugins/vis_type_vega/public/components/vega_vis_editor.tsx
@@ -13,7 +13,7 @@ import hjson from 'hjson';
import 'brace/mode/hjson';
import { i18n } from '@kbn/i18n';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { getNotifications } from '../services';
import { VisParams } from '../vega_fn';
import { VegaHelpMenu } from './vega_help_menu';
@@ -55,7 +55,7 @@ function format(
}
}
-function VegaVisEditor({ stateParams, setValue }: VisOptionsProps) {
+function VegaVisEditor({ stateParams, setValue }: VisEditorOptionsProps) {
const onChange = useCallback(
(value: string) => {
setValue('spec', value);
diff --git a/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx b/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx
index 875f13453cf6d0..bd26424552038b 100644
--- a/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx
+++ b/src/plugins/vis_type_vega/public/components/vega_vis_editor_lazy.tsx
@@ -8,11 +8,11 @@
import React, { lazy } from 'react';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { VisParams } from '../vega_fn';
const VegaVisEditor = lazy(() => import('./vega_vis_editor'));
-export const VegaVisEditorComponent = (props: VisOptionsProps) => (
+export const VegaVisEditorComponent = (props: VisEditorOptionsProps) => (
);
diff --git a/src/plugins/vis_type_vega/public/plugin.ts b/src/plugins/vis_type_vega/public/plugin.ts
index b74b686500e6d3..376ef84de23c32 100644
--- a/src/plugins/vis_type_vega/public/plugin.ts
+++ b/src/plugins/vis_type_vega/public/plugin.ts
@@ -84,7 +84,7 @@ export class VegaPlugin implements Plugin, void> {
expressions.registerFunction(() => createVegaFn(visualizationDependencies));
expressions.registerRenderer(getVegaVisRenderer(visualizationDependencies));
- visualizations.createBaseVisualization(createVegaTypeDefinition(visualizationDependencies));
+ visualizations.createBaseVisualization(createVegaTypeDefinition());
}
public start(core: CoreStart, { data }: VegaPluginStartDependencies) {
diff --git a/src/plugins/vis_type_vega/public/vega_type.ts b/src/plugins/vis_type_vega/public/vega_type.ts
index 66e4b8b98056f8..4b2ec658500301 100644
--- a/src/plugins/vis_type_vega/public/vega_type.ts
+++ b/src/plugins/vis_type_vega/public/vega_type.ts
@@ -8,16 +8,13 @@
import { i18n } from '@kbn/i18n';
import { parse } from 'hjson';
-import type { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
import { DefaultEditorSize } from '../../vis_default_editor/public';
-import type { VegaVisualizationDependencies } from './plugin';
+import { VIS_EVENT_TO_TRIGGER, VisGroups, VisTypeDefinition } from '../../visualizations/public';
-import { createVegaRequestHandler } from './vega_request_handler';
import { getDefaultSpec } from './default_spec';
import { extractIndexPatternsFromSpec } from './lib/extract_index_pattern';
import { createInspectorAdapters } from './vega_inspector';
-import { VIS_EVENT_TO_TRIGGER, VisGroups } from '../../visualizations/public';
import { toExpressionAst } from './to_ast';
import { getInfoMessage } from './components/experimental_map_vis_info';
import { VegaVisEditorComponent } from './components/vega_vis_editor_lazy';
@@ -25,11 +22,7 @@ import { VegaVisEditorComponent } from './components/vega_vis_editor_lazy';
import type { VegaSpec } from './data_model/types';
import type { VisParams } from './vega_fn';
-export const createVegaTypeDefinition = (
- dependencies: VegaVisualizationDependencies
-): BaseVisTypeOptions => {
- const requestHandler = createVegaRequestHandler(dependencies);
-
+export const createVegaTypeDefinition = (): VisTypeDefinition => {
return {
name: 'vega',
title: 'Vega',
@@ -52,7 +45,6 @@ export const createVegaTypeDefinition = (
enableAutoApply: true,
defaultSize: DefaultEditorSize.MEDIUM,
},
- requestHandler,
toExpressionAst,
options: {
showIndexSelection: false,
@@ -73,5 +65,9 @@ export const createVegaTypeDefinition = (
return [];
},
inspectorAdapters: createInspectorAdapters,
+ /**
+ * This is necessary for showing actions bar in top of vega editor
+ */
+ requiresSearch: true,
};
};
diff --git a/src/plugins/vis_type_vislib/public/area.ts b/src/plugins/vis_type_vislib/public/area.ts
index 7458d0a44ecae4..cbb705f4cce5dc 100644
--- a/src/plugins/vis_type_vislib/public/area.ts
+++ b/src/plugins/vis_type_vislib/public/area.ts
@@ -7,13 +7,12 @@
*/
import { xyVisTypes } from '../../vis_type_xy/public';
-import { BaseVisTypeOptions } from '../../visualizations/public';
+import { VisTypeDefinition } from '../../visualizations/public';
import { toExpressionAst } from './to_ast';
import { BasicVislibParams } from './types';
-export const areaVisTypeDefinition: BaseVisTypeOptions = {
- ...(xyVisTypes.area() as BaseVisTypeOptions),
+export const areaVisTypeDefinition = {
+ ...xyVisTypes.area(),
toExpressionAst,
- visualization: undefined,
-};
+} as VisTypeDefinition;
diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx
index fa0091b2082b76..e9651954ed3024 100644
--- a/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx
+++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/index.tsx
@@ -9,20 +9,20 @@
import React, { useCallback } from 'react';
import { EuiSpacer } from '@elastic/eui';
-import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { GaugeVisParams } from '../../../gauge';
import { RangesPanel } from './ranges_panel';
import { StylePanel } from './style_panel';
import { LabelsPanel } from './labels_panel';
-export type GaugeOptionsInternalProps = VisOptionsProps & {
+export type GaugeOptionsInternalProps = VisEditorOptionsProps & {
setGaugeValue: (
paramName: T,
value: GaugeVisParams['gauge'][T]
) => void;
};
-function GaugeOptions(props: VisOptionsProps) {
+function GaugeOptions(props: VisEditorOptionsProps) {
const { stateParams, setValue } = props;
const setGaugeValue: GaugeOptionsInternalProps['setGaugeValue'] = useCallback(
diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx
index 7092615d9fcafa..2dd4b06a52288e 100644
--- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx
+++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx
@@ -12,9 +12,9 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { ValueAxis } from '../../../../../vis_type_xy/public';
import {
- VisOptionsProps,
BasicOptions,
SelectOption,
SwitchOption,
@@ -28,7 +28,7 @@ import {
import { HeatmapVisParams } from '../../../heatmap';
import { LabelsPanel } from './labels_panel';
-function HeatmapOptions(props: VisOptionsProps) {
+function HeatmapOptions(props: VisEditorOptionsProps) {
const { stateParams, vis, uiState, setValue, setValidity, setTouched } = props;
const [valueAxis] = stateParams.valueAxes;
const isColorsNumberInvalid = stateParams.colorsNumber < 2 || stateParams.colorsNumber > 10;
diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx
index cc224f8e1c6aa1..ff98a494ad9067 100644
--- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx
+++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx
@@ -12,7 +12,8 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { VisOptionsProps, SwitchOption } from '../../../../../vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
+import { SwitchOption } from '../../../../../vis_default_editor/public';
import { ValueAxis } from '../../../../../vis_type_xy/public';
import { HeatmapVisParams } from '../../../heatmap';
@@ -21,7 +22,7 @@ const VERTICAL_ROTATION = 270;
interface LabelsPanelProps {
valueAxis: ValueAxis;
- setValue: VisOptionsProps['setValue'];
+ setValue: VisEditorOptionsProps['setValue'];
}
function LabelsPanel({ valueAxis, setValue }: LabelsPanelProps) {
diff --git a/src/plugins/vis_type_vislib/public/editor/components/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/index.tsx
index 264c3066f13fe6..6c6181246d6db1 100644
--- a/src/plugins/vis_type_vislib/public/editor/components/index.tsx
+++ b/src/plugins/vis_type_vislib/public/editor/components/index.tsx
@@ -8,8 +8,7 @@
import React, { lazy } from 'react';
-import { VisOptionsProps } from '../../../../vis_default_editor/public';
-
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { GaugeVisParams } from '../../gauge';
import { PieVisParams } from '../../pie';
import { HeatmapVisParams } from '../../heatmap';
@@ -18,12 +17,14 @@ const GaugeOptionsLazy = lazy(() => import('./gauge'));
const PieOptionsLazy = lazy(() => import('./pie'));
const HeatmapOptionsLazy = lazy(() => import('./heatmap'));
-export const GaugeOptions = (props: VisOptionsProps) => (
+export const GaugeOptions = (props: VisEditorOptionsProps) => (
);
-export const PieOptions = (props: VisOptionsProps) => ;
+export const PieOptions = (props: VisEditorOptionsProps) => (
+
+);
-export const HeatmapOptions = (props: VisOptionsProps) => (
+export const HeatmapOptions = (props: VisEditorOptionsProps) => (
);
diff --git a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx
index 863b11c6a3fcfb..e0ea1fcee4881c 100644
--- a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx
+++ b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx
@@ -12,12 +12,13 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { BasicOptions, SwitchOption, VisOptionsProps } from '../../../../vis_default_editor/public';
+import { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
+import { BasicOptions, SwitchOption } from '../../../../vis_default_editor/public';
import { TruncateLabelsOption } from '../../../../vis_type_xy/public';
import { PieVisParams } from '../../pie';
-function PieOptions(props: VisOptionsProps) {
+function PieOptions(props: VisEditorOptionsProps) {
const { stateParams, setValue } = props;
const setLabels =