diff --git a/.browserslistrc b/.browserslistrc
index 36298c0f8cb93..c54816e60aebe 100644
--- a/.browserslistrc
+++ b/.browserslistrc
@@ -2,10 +2,10 @@
last 2 Firefox versions
last 2 Chrome versions
last 2 Safari versions
-> 0.25%
-not ie 11
-not op_mini all
-not samsung 4
+last 2 Edge versions
+last 1 ios_saf versions
+last 1 and_chr versions
+last 1 samsung versions
[dev]
last 1 chrome versions
diff --git a/.ci/teamcity/default/build.sh b/.ci/teamcity/default/build.sh
index af90e24ef5fe8..140233f29e6af 100755
--- a/.ci/teamcity/default/build.sh
+++ b/.ci/teamcity/default/build.sh
@@ -14,6 +14,7 @@ node scripts/build_kibana_platform_plugins \
--scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \
--scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \
--scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \
+ --scan-dir "$XPACK_DIR/test/usage_collection/plugins" \
--verbose
tc_end_block "Build Platform Plugins"
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index efba93350b8fb..2a5fc914662b6 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -11,7 +11,7 @@ Delete any items that are not applicable to this PR.
- [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
-- [ ] If a plugin configuration key changed, check if it needs to be whitelisted in the [cloud](https://github.com/elastic/cloud) and added to the [docker list](https://github.com/elastic/kibana/blob/c29adfef29e921cc447d2a5ed06ac2047ceab552/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker)
+- [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the [cloud](https://github.com/elastic/cloud) and added to the [docker list](https://github.com/elastic/kibana/blob/c29adfef29e921cc447d2a5ed06ac2047ceab552/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
diff --git a/.teamcity/src/Common.kt b/.teamcity/src/Common.kt
index 2e5357541bfe7..de3f96a5c790f 100644
--- a/.teamcity/src/Common.kt
+++ b/.teamcity/src/Common.kt
@@ -22,6 +22,14 @@ fun isReportingEnabled(): Boolean {
return ENABLE_REPORTING;
}
+// master and 7.x get committed to so often, we only want to run full CI for them hourly
+// but for other branches, we can run daily and on merge
+fun isHourlyOnlyBranch(): Boolean {
+ val branch = getProjectBranch()
+
+ return branch == "master" || branch.matches("""^[0-9]+\.x$""".toRegex())
+}
+
fun makeSafeId(id: String): String {
return id.replace(Regex("[^a-zA-Z0-9_]"), "_")
}
diff --git a/.teamcity/src/builds/DailyCi.kt b/.teamcity/src/builds/DailyCi.kt
new file mode 100644
index 0000000000000..9a8f25f5ba014
--- /dev/null
+++ b/.teamcity/src/builds/DailyCi.kt
@@ -0,0 +1,37 @@
+package builds
+
+import addSlackNotifications
+import areTriggersEnabled
+import dependsOn
+import getProjectBranch
+import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
+import jetbrains.buildServer.configs.kotlin.v2019_2.FailureAction
+import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.schedule
+
+object DailyCi : BuildType({
+ id("Daily_CI")
+ name = "Daily CI"
+ description = "Runs everything in CI, daily"
+ type = Type.COMPOSITE
+ paused = !areTriggersEnabled()
+
+ triggers {
+ schedule {
+ schedulingPolicy = cron {
+ hours = "0"
+ minutes = "0"
+ }
+ branchFilter = "refs/heads/${getProjectBranch()}"
+ triggerBuild = always()
+ withPendingChangesOnly = false
+ }
+ }
+
+ dependsOn(
+ FullCi
+ ) {
+ onDependencyCancel = FailureAction.ADD_PROBLEM
+ }
+
+ addSlackNotifications()
+})
diff --git a/.teamcity/src/builds/OnMergeCi.kt b/.teamcity/src/builds/OnMergeCi.kt
new file mode 100644
index 0000000000000..174b73d53de61
--- /dev/null
+++ b/.teamcity/src/builds/OnMergeCi.kt
@@ -0,0 +1,34 @@
+package builds
+
+import addSlackNotifications
+import areTriggersEnabled
+import dependsOn
+import getProjectBranch
+import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
+import jetbrains.buildServer.configs.kotlin.v2019_2.FailureAction
+import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
+
+object OnMergeCi : BuildType({
+ id("OnMerge_CI")
+ name = "On Merge CI"
+ description = "Runs everything in CI, on each commit"
+ type = Type.COMPOSITE
+ paused = !areTriggersEnabled()
+
+ maxRunningBuilds = 1
+
+ triggers {
+ vcs {
+ perCheckinTriggering = false
+ branchFilter = "refs/heads/${getProjectBranch()}"
+ }
+ }
+
+ dependsOn(
+ FullCi
+ ) {
+ onDependencyCancel = FailureAction.ADD_PROBLEM
+ }
+
+ addSlackNotifications()
+})
diff --git a/.teamcity/src/builds/default/DefaultFunctionalBase.kt b/.teamcity/src/builds/default/DefaultFunctionalBase.kt
index d8124bd8521c0..dc2f7756efeb5 100644
--- a/.teamcity/src/builds/default/DefaultFunctionalBase.kt
+++ b/.teamcity/src/builds/default/DefaultFunctionalBase.kt
@@ -1,6 +1,8 @@
package builds.default
+import StandardAgents
import addTestSettings
+import co.elastic.teamcity.common.requireAgent
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
open class DefaultFunctionalBase(init: BuildType.() -> Unit = {}) : BuildType({
@@ -8,6 +10,8 @@ open class DefaultFunctionalBase(init: BuildType.() -> Unit = {}) : BuildType({
param("env.KBN_NP_PLUGINS_BUILT", "true")
}
+ requireAgent(StandardAgents["4"]!!)
+
dependencies {
defaultBuildWithPlugins()
}
diff --git a/.teamcity/src/projects/Kibana.kt b/.teamcity/src/projects/Kibana.kt
index fe04d4f5ab36e..5cddcf18e067f 100644
--- a/.teamcity/src/projects/Kibana.kt
+++ b/.teamcity/src/projects/Kibana.kt
@@ -7,6 +7,7 @@ import builds.oss.*
import builds.test.*
import CloudProfile
import co.elastic.teamcity.common.googleCloudProfile
+import isHourlyOnlyBranch
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.projectFeatures.slackConnection
import templates.KibanaTemplate
@@ -136,7 +137,16 @@ fun Kibana(config: KibanaConfiguration = KibanaConfiguration()) : Project {
buildType(FullCi)
buildType(BaselineCi)
- buildType(HourlyCi)
+
+ // master and 7.x get committed to so often, we only want to run full CI for them hourly
+ // but for other branches, we can run daily and on merge
+ if (isHourlyOnlyBranch()) {
+ buildType(HourlyCi)
+ } else {
+ buildType(DailyCi)
+ buildType(OnMergeCi)
+ }
+
buildType(PullRequestCi)
}
diff --git a/dev_docs/assets/kibana_platform_plugin_end_user.png b/dev_docs/assets/kibana_platform_plugin_end_user.png
new file mode 100644
index 0000000000000..a0e32a35ffe60
Binary files /dev/null and b/dev_docs/assets/kibana_platform_plugin_end_user.png differ
diff --git a/dev_docs/assets/platform_plugin_cycle.png b/dev_docs/assets/platform_plugin_cycle.png
new file mode 100644
index 0000000000000..533a38c4ed9c4
Binary files /dev/null and b/dev_docs/assets/platform_plugin_cycle.png differ
diff --git a/dev_docs/kibana_platform_plugin_intro.mdx b/dev_docs/kibana_platform_plugin_intro.mdx
new file mode 100644
index 0000000000000..3303561fae069
--- /dev/null
+++ b/dev_docs/kibana_platform_plugin_intro.mdx
@@ -0,0 +1,305 @@
+---
+id: kibPlatformIntro
+slug: /kibana-dev-docs/platform-intro
+title: Plugins and the Kibana platform
+summary: An introduction to the Kibana platform and how to use it to build a plugin.
+date: 2021-01-06
+tags: ['kibana','onboarding', 'dev', 'architecture']
+---
+
+From an end user perspective, Kibana is a tool for interacting with Elasticsearch, providing an easy way
+to visualize and analyze data.
+
+From a developer perspective, Kibana is a platform that provides a set of tools to build not only the UI you see in Kibana today, but
+a wide variety of applications that can be used to explore, visualize, and act upon data in Elasticsearch. The platform provides developers the ability to build applications, or inject extra functionality into
+already existing applications. Did you know that almost everything you see in the
+Kibana UI is built inside a plugin? If you removed all plugins from Kibana, you'd be left with an empty navigation menu, and a set of
+developer tools. The Kibana platform is a blank canvas, just waiting for a developer to come along and create something!
+
+![Kibana personas](assets/kibana_platform_plugin_end_user.png)
+
+## Plugins vs The Platform
+
+The core platform provides the most basic and fundamental tools neccessary for building a plugin, like creating saved objects,
+routing, application registration, and notifications. The Core platform is not a plugin itself, although
+there are some plugins that provide platform functionality. For example, the
+ provides basic utilities to search, query, and filter data in Elasticsearch.
+This code is not part of Core, but is still fundamental for building a plugin,
+ and we strongly encourage using this service over querying Elasticsearch directly.
+
+
+We currently have three kinds of public services:
+
+ - platform services provided by `core`
+ - platform services provided by plugins, that can, and should, be used by every plugin (e.g. ) .
+ - shared services provided by plugins, that are only relevant for only a few, specific plugins (e.g. "presentation utils").
+
+Two common questions we encounter are:
+
+1. Which services are platform services?
+2. What is the difference between platform code supplied by core, and platform code supplied by plugins?
+
+We don't have great answers to those questions today. Currently, the best answers we have are:
+
+1. Platform plugins are _usually_ plugins that are managed by the Platform Group, but we are starting to see some exceptions.
+2. `core` code contains the most fundamental and stable services needed for plugin development. Everything else goes in a plugin.
+
+We will continue to focus on adding clarity around these types of services and what developers can expect from each.
+
+
+
+
+
+When the Kibana platform and plugin infrastructure was built, we thought of two types of code: core services, and other plugin services. We planned to keep the most stable and fundamental
+code needed to build plugins inside core.
+
+In reality, we ended up with many platform-like services living outside of core, with no (short term) intention of moving them. We highly encourage plugin developers to use
+them, so we consider them part of platform services.
+
+When we built our platform system, we also thought we'd end up with only a handful of large plugins outside core. Users could turn certain plugins off, to minimize the code
+ footprint and speed up Kibana.
+
+In reality, our plugin model ended up being used like micro-services. Plugins are the only form of encapsulation we provide developers, and they liked it! However, we ended
+ up with a ton of small plugins, that developers never intended to be uninstallable, nor tested in this manner. We are considering ways to provide developers the ability to build services
+ with the encapsulation
+ they desire, without the need to build a plugin.
+
+Another side effect of having many small plugins is that common code often ends up extracted into another plugin. Use case specific utilities are exported,
+ that are not meant to be used in a general manner. This makes our definition of "platform code" a bit trickier to define. We'd like to say "The platform is made up of
+ every publically exposed service", but in today's world, that wouldn't be a very accurate picture.
+
+We recognize the need to better clarify the relationship between core functionality, platform-like plugin functionality, and functionality exposed by other plugins.
+ It's something we will be working on!
+
+
+
+The main difference between core functionality and functionality supplied by plugins, is in how it is accessed. Core is
+passed to plugins as the first parameter to their `start` and `setup` lifecycle functions, while plugin supplied functionality is passed as the
+second parameter. Plugin dependencies must be declared explicitly inside the `kibana.json` file. Core functionality is always provided. Read the
+section on for more information.
+
+## The anatomy of a plugin
+
+Plugins are defined as classes and present themselves to Kibana through a simple wrapper function. A plugin can have browser-side code, server-side code,
+or both. There is no architectural difference between a plugin in the browser and a plugin on the server. In both places, you describe your plugin similarly,
+and you interact with Core and other plugins in the same way.
+
+The basic file structure of a Kibana plugin named demo that has both client-side and server-side code would be:
+
+```
+plugins/
+ demo
+ kibana.json [1]
+ public
+ index.ts [2]
+ plugin.ts [3]
+ server
+ index.ts [4]
+ plugin.ts [5]
+```
+
+### [1] kibana.json
+
+`kibana.json` is a static manifest file that is used to identify the plugin and to specify if this plugin has server-side code, browser-side code, or both:
+
+```
+{
+ "id": "demo",
+ "version": "kibana",
+ "server": true,
+ "ui": true
+}
+```
+
+### [2] public/index.ts
+
+`public/index.ts` is the entry point into the client-side code of this plugin. It must export a function named plugin, which will receive a standard set of
+ core capabilities as an argument. It should return an instance of its plugin class for Kibana to load.
+
+```
+import type { PluginInitializerContext } from 'kibana/server';
+import { DemoPlugin } from './plugin';
+
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new DemoPlugin(initializerContext);
+}
+```
+
+### [3] public/plugin.ts
+
+`public/plugin.ts` is the client-side plugin definition itself. Technically speaking, it does not need to be a class or even a separate file from the entry
+ point, but all plugins at Elastic should be consistent in this way.
+
+
+ ```ts
+import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';
+
+export class DemoPlugin implements Plugin {
+ constructor(initializerContext: PluginInitializerContext) {}
+
+ public setup(core: CoreSetup) {
+ // called when plugin is setting up during Kibana's startup sequence
+ }
+
+ public start(core: CoreStart) {
+ // called after all plugins are set up
+ }
+
+ public stop() {
+ // called when plugin is torn down during Kibana's shutdown sequence
+ }
+}
+ ```
+
+
+### [4] server/index.ts
+
+`server/index.ts` is the entry-point into the server-side code of this plugin. It is identical in almost every way to the client-side entry-point:
+
+### [5] server/plugin.ts
+
+`server/plugin.ts` is the server-side plugin definition. The shape of this plugin is the same as it’s client-side counter-part:
+
+```ts
+import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';
+
+export class DemoPlugin implements Plugin {
+ constructor(initializerContext: PluginInitializerContext) {}
+
+ public setup(core: CoreSetup) {
+ // called when plugin is setting up during Kibana's startup sequence
+ }
+
+ public start(core: CoreStart) {
+ // called after all plugins are set up
+ }
+
+ public stop() {
+ // called when plugin is torn down during Kibana's shutdown sequence
+ }
+}
+```
+
+Kibana does not impose any technical restrictions on how the the internals of a plugin are architected, though there are certain
+considerations related to how plugins integrate with core APIs and APIs exposed by other plugins that may greatly impact how they are built.
+
+## Plugin lifecycles & Core services
+
+The various independent domains that make up core are represented by a series of services. Those services expose public interfaces that are provided to all plugins.
+Services expose different features at different parts of their lifecycle. We describe the lifecycle of core services and plugins with specifically-named functions on the service definition.
+
+Kibana has three lifecycles: setup, start, and stop. Each plugin’s setup function is called sequentially while Kibana is setting up on the server or when it is being loaded in the browser. The start functions are called sequentially after setup has been completed for all plugins. The stop functions are called sequentially while Kibana is gracefully shutting down the server or when the browser tab or window is being closed.
+
+The table below explains how each lifecycle relates to the state of Kibana.
+
+| lifecycle | purpose | server | browser |
+| ---------- | ------ | ------- | ----- |
+| setup | perform "registration" work to setup environment for runtime |configure REST API endpoint, register saved object types, etc. | configure application routes in SPA, register custom UI elements in extension points, etc. |
+| start | bootstrap runtime logic | respond to an incoming request, request Elasticsearch server, etc. | start polling Kibana server, update DOM tree in response to user interactions, etc.|
+| stop | cleanup runtime | dispose of active handles before the server shutdown. | store session data in the LocalStorage when the user navigates away from Kibana, etc. |
+
+Different service interfaces can and will be passed to setup, start, and stop because certain functionality makes sense in the context of a running plugin while other types
+of functionality may have restrictions or may only make sense in the context of a plugin that is stopping.
+
+## How plugin's interact with each other, and Core
+
+The lifecycle-specific contracts exposed by core services are always passed as the first argument to the equivalent lifecycle function in a plugin.
+For example, the core http service exposes a function createRouter to all plugin setup functions. To use this function to register an HTTP route handler,
+a plugin just accesses it off of the first argument:
+
+```ts
+import type { CoreSetup } from 'kibana/server';
+
+export class DemoPlugin {
+ public setup(core: CoreSetup) {
+ const router = core.http.createRouter();
+ // handler is called when '/path' resource is requested with `GET` method
+ router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' }));
+ }
+}
+```
+
+Unlike core, capabilities exposed by plugins are not automatically injected into all plugins.
+Instead, if a plugin wishes to use the public interface provided by another plugin, it must first declare that plugin as a
+ dependency in it’s kibana.json manifest file.
+
+** foobar plugin.ts: **
+
+```
+import type { Plugin } from 'kibana/server';
+export interface FoobarPluginSetup { [1]
+ getFoo(): string;
+}
+
+export interface FoobarPluginStart { [1]
+ getBar(): string;
+}
+
+export class MyPlugin implements Plugin {
+ public setup(): FoobarPluginSetup {
+ return {
+ getFoo() {
+ return 'foo';
+ },
+ };
+ }
+
+ public start(): FoobarPluginStart {
+ return {
+ getBar() {
+ return 'bar';
+ },
+ };
+ }
+}
+```
+[1] We highly encourage plugin authors to explicitly declare public interfaces for their plugins.
+
+
+** demo kibana.json**
+
+```
+{
+ "id": "demo",
+ "requiredPlugins": ["foobar"],
+ "server": true,
+ "ui": true
+}
+```
+
+With that specified in the plugin manifest, the appropriate interfaces are then available via the second argument of setup and/or start:
+
+```ts
+import type { CoreSetup, CoreStart } from 'kibana/server';
+import type { FoobarPluginSetup, FoobarPluginStart } from '../../foobar/server';
+
+interface DemoSetupPlugins { [1]
+ foobar: FoobarPluginSetup;
+}
+
+interface DemoStartPlugins {
+ foobar: FoobarPluginStart;
+}
+
+export class DemoPlugin {
+ public setup(core: CoreSetup, plugins: DemoSetupPlugins) { [2]
+ const { foobar } = plugins;
+ foobar.getFoo(); // 'foo'
+ foobar.getBar(); // throws because getBar does not exist
+ }
+
+ public start(core: CoreStart, plugins: DemoStartPlugins) { [3]
+ const { foobar } = plugins;
+ foobar.getFoo(); // throws because getFoo does not exist
+ foobar.getBar(); // 'bar'
+ }
+
+ public stop() {}
+}
+```
+
+[1] The interface for plugin’s dependencies must be manually composed. You can do this by importing the appropriate type from the plugin and constructing an interface where the property name is the plugin’s ID.
+
+[2] These manually constructed types should then be used to specify the type of the second argument to the plugin.
+
+[3] Notice that the type for the setup and start lifecycles are different. Plugin lifecycle functions can only access the APIs that are exposed during that lifecycle.
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md
new file mode 100644
index 0000000000000..0fdf36bc719ec
--- /dev/null
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md)
+
+## ExpressionsInspectorAdapter.ast property
+
+Signature:
+
+```typescript
+get ast(): any;
+```
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md
new file mode 100644
index 0000000000000..671270a5c78ce
--- /dev/null
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md
@@ -0,0 +1,22 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [logAST](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md)
+
+## ExpressionsInspectorAdapter.logAST() method
+
+Signature:
+
+```typescript
+logAST(ast: any): void;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| ast | any | |
+
+Returns:
+
+`void`
+
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md
new file mode 100644
index 0000000000000..23d542a0f69eb
--- /dev/null
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md
@@ -0,0 +1,24 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md)
+
+## ExpressionsInspectorAdapter class
+
+Signature:
+
+```typescript
+export declare class ExpressionsInspectorAdapter extends EventEmitter
+```
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md) | | any | |
+
+## Methods
+
+| Method | Modifiers | Description |
+| --- | --- | --- |
+| [logAST(ast)](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md) | | |
+
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md
index 1b97c9e11f83c..e3eb7a34175ee 100644
--- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md
@@ -16,6 +16,7 @@
| [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) | |
| [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) | |
| [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) | |
+| [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) | |
| [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) | |
| [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) | ExpressionsService class is used for multiple purposes:1. It implements the same Expressions service that can be used on both: (1) server-side and (2) browser-side. 2. It implements the same Expressions service that users can fork/clone, thus have their own instance of the Expressions plugin. 3. ExpressionsService defines the public contracts of \*setup\* and \*start\* Kibana Platform life-cycles for ease-of-use on server-side and browser-side. 4. ExpressionsService creates a bound version of all exported contract functions. 5. Functions are bound the way there are:\`\`\`ts registerFunction = (...args: Parameters<Executor\['registerFunction'\]> ): ReturnType<Executor\['registerFunction'\]> => this.executor.registerFunction(...args); \`\`\`so that JSDoc appears in developers IDE when they use those plugins.expressions.registerFunction(. |
| [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) | |
diff --git a/docs/index.asciidoc b/docs/index.asciidoc
index b91af2ab51ebf..eb6f794434f8a 100644
--- a/docs/index.asciidoc
+++ b/docs/index.asciidoc
@@ -7,7 +7,7 @@
:blog-ref: https://www.elastic.co/blog/
:wikipedia: https://en.wikipedia.org/wiki
-include::{docs-root}/shared/versions/stack/7.10.asciidoc[]
+include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[]
:docker-repo: docker.elastic.co/kibana/kibana
:docker-image: docker.elastic.co/kibana/kibana:{version}
diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc
index 7e7c8953fd527..c2306b80734d8 100644
--- a/docs/management/advanced-options.asciidoc
+++ b/docs/management/advanced-options.asciidoc
@@ -458,7 +458,7 @@ of buckets to try to represent.
[horizontal]
[[visualization-visualize-chartslibrary]]`visualization:visualize:legacyChartsLibrary`::
-Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation.
+Enables legacy charts library for area, line and bar charts in visualize.
[[visualization-colormapping]]`visualization:colorMapping`::
**This setting is deprecated and will not be supported as of 8.0.**
diff --git a/docs/management/managing-tags.asciidoc b/docs/management/managing-tags.asciidoc
index 3da98b2281fdc..88fdef66a7418 100644
--- a/docs/management/managing-tags.asciidoc
+++ b/docs/management/managing-tags.asciidoc
@@ -37,8 +37,7 @@ Create a tag to assign to your saved objects.
image::images/tags/create-tag.png[Tag creation popin]
. Enter a name and select a color for the new tag.
+
-The name can include alphanumeric characters (English letters and digits), `:`, `-`, `_` and the space character,
-and cannot be longer than 50 characters.
+The name cannot be longer than 50 characters.
. Click *Create tag*.
[float]
diff --git a/docs/setup/upgrade/upgrade-migrations.asciidoc b/docs/setup/upgrade/upgrade-migrations.asciidoc
index 74d097164c4a7..7436536d22781 100644
--- a/docs/setup/upgrade/upgrade-migrations.asciidoc
+++ b/docs/setup/upgrade/upgrade-migrations.asciidoc
@@ -1,11 +1,13 @@
[[upgrade-migrations]]
=== Upgrade migrations
-Every time {kib} is upgraded it checks to see if all saved objects, such as dashboards, visualizations, and index patterns, are compatible with the new version. If any saved objects need to be updated, then the automatic saved object migration process is kicked off.
+Every time {kib} is upgraded it will perform an upgrade migration to ensure that all <> are compatible with the new version.
NOTE: 6.7 includes an https://www.elastic.co/guide/en/kibana/6.7/upgrade-assistant.html[Upgrade Assistant]
to help you prepare for your upgrade to 7.0. To access the assistant, go to *Management > 7.0 Upgrade Assistant*.
+WARNING: {kib} 7.12.0 and later uses a new migration process and index naming scheme. Be sure to read the documentation for your version of {kib} before proceeding.
+
WARNING: The following instructions assumes {kib} is using the default index names. If the `kibana.index` or `xpack.tasks.index` configuration settings were changed these instructions will have to be adapted accordingly.
[float]
@@ -14,19 +16,35 @@ WARNING: The following instructions assumes {kib} is using the default index nam
Saved objects are stored in two indices:
-* `.kibana_N`, or if set, the `kibana.index` configuration setting
-* `.kibana_task_manager_N`, or if set, the `xpack.tasks.index` configuration setting
+* `.kibana_{kibana_version}_001`, or if the `kibana.index` configuration setting is set `.{kibana.index}_{kibana_version}_001`. E.g. for Kibana v7.12.0 `.kibana_7.12.0_001`.
+* `.kibana_task_manager_{kibana_version}_001`, or if the `xpack.tasks.index` configuration setting is set `.{xpack.tasks.index}_{kibana_version}_001` E.g. for Kibana v7.12.0 `.kibana_task_manager_7.12.0_001`.
-For each of these indices, `N` is a number that increments every time {kib} runs an upgrade migration on that index. The index aliases `.kibana` and `.kibana_task_manager` point to the most up-to-date index.
+The index aliases `.kibana` and `.kibana_task_manager` will always point to the most up-to-date version indices.
+
+The first time a newer {kib} starts, it will first perform an upgrade migration before starting plugins or serving HTTP traffic. To prevent losing acknowledged writes old nodes should be shutdown before starting the upgrade. To reduce the likelihood of old nodes losing acknowledged writes, {kib} 7.12.0 and later will add a write block to the outdated index. Table 1 lists the saved objects indices used by previous versions of {kib}.
+
+.Saved object indices and aliases per {kib} version
+[options="header"]
+[cols="a,a,a"]
+|=======================
+|Upgrading from version | Outdated index (alias) | Upgraded index (alias)
+| 6.0.0 through 6.4.x | `.kibana` 1.3+^.^| `.kibana_7.12.0_001`
+(`.kibana` alias)
+
+`.kibana_task_manager_7.12.0_001` (`.kibana_task_manager` alias)
+| 6.5.0 through 7.3.x | `.kibana_N` (`.kibana` alias)
+| 7.4.0 through 7.11.x
+| `.kibana_N` (`.kibana` alias)
-While {kib} is starting up and before serving any HTTP traffic, it checks to see if any internal mapping changes or data transformations for existing saved objects are required.
+`.kibana_task_manager_N` (`.kibana_task_manager` alias)
+|=======================
-When changes are necessary, a new migration is started. To ensure that only one {kib} instance performs the migration, each instance will attempt to obtain a migration lock by creating a new `.kibana_N+1` index. The instance that succeeds in creating the index will then read batches of documents from the existing index, migrate them, and write them to the new index. Once the objects are migrated, the lock is released by pointing the `.kibana` index alias the new upgraded `.kibana_N+1` index.
+==== Upgrading multiple {kib} instances
+When upgrading several {kib} instances connected to the same {es} cluster, ensure that all outdated instances are shutdown before starting the upgrade.
-Instances that failed to acquire a lock will log `Another Kibana instance appears to be migrating the index. Waiting for that migration to complete`. The instance will then wait until `.kibana` points to an upgraded index before starting up and serving HTTP traffic.
+Kibana does not support rolling upgrades. However, once outdated instances are shutdown, all upgraded instances can be started in parallel in which case all instances will participate in the upgrade migration in parallel.
-NOTE: Prior to 6.5.0, saved objects were stored directly in an index named `.kibana`. After upgrading to version 6.5+, {kib} will migrate this index into `.kibana_N` and set `.kibana` up as an index alias. +
-Prior to 7.4.0, task manager tasks were stored directly in an index name `.kibana_task_manager`. After upgrading to version 7.4+, {kib} will migrate this index into `.kibana_task_manager_N` and set `.kibana_task_manager` up as an index alias.
+For large deployments with more than 10 {kib} instances and more than 10 000 saved objects, the upgrade downtime can be reduced by bringing up a single {kib} instance and waiting for it to complete the upgrade migration before bringing up the remaining instances.
[float]
[[preventing-migration-failures]]
@@ -54,50 +72,31 @@ Problems with your {es} cluster can prevent {kib} upgrades from succeeding. Ensu
* a "green" cluster status
[float]
-===== Running different versions of {kib} connected to the same {es} index
-Kibana does not support rolling upgrades. Stop all {kib} instances before starting a newer version to prevent upgrade failures and data loss.
+===== Different versions of {kib} connected to the same {es} index
+When different versions of {kib} are attempting an upgrade migration in parallel this can lead to migration failures. Ensure that all {kib} instances are running the same version, configuration and plugins.
[float]
===== Incompatible `xpack.tasks.index` configuration setting
-For {kib} < 7.5.1, if the task manager index is set to `.tasks` with the configuration setting `xpack.tasks.index: ".tasks"`, upgrade migrations will fail. {kib} 7.5.1 and later prevents this by refusing to start with an incompatible configuration setting.
+For {kib} versions prior to 7.5.1, if the task manager index is set to `.tasks` with the configuration setting `xpack.tasks.index: ".tasks"`, upgrade migrations will fail. {kib} 7.5.1 and later prevents this by refusing to start with an incompatible configuration setting.
[float]
[[resolve-migrations-failures]]
==== Resolving migration failures
-If {kib} terminates unexpectedly while migrating a saved object index, manual intervention is required before {kib} will attempt to perform the migration again. Follow the advice in (preventing migration failures)[preventing-migration-failures] before retrying a migration upgrade.
-
-As mentioned above, {kib} will create a migration lock for each index that requires a migration by creating a new `.kibana_N+1` index. For example: if the `.kibana_task_manager` alias is pointing to `.kibana_task_manager_5` then the first {kib} that succeeds in creating `.kibana_task_manager_6` will obtain the lock to start migrations.
-
-However, if the instance that obtained the lock fails to migrate the index, all other {kib} instances will be blocked from performing this migration. This includes the instance that originally obtained the lock, it will be blocked from retrying the migration even when restarted.
-
-[float]
-===== Retry a migration by restoring a backup snapshot:
-
-1. Before proceeding ensure that you have a recent and successful backup snapshot of all `.kibana*` indices.
-2. Shutdown all {kib} instances to be 100% sure that there are no instances currently performing a migration.
-3. Delete all saved object indices with `DELETE /.kibana*`
-4. Restore the `.kibana* indices and their aliases from the backup snapshot. See {es} {ref}/modules-snapshots.html[snapshots]
-5. Start up all {kib} instances to retry the upgrade migration.
-
-[float]
-===== (Not recommended) Retry a migration without a backup snapshot:
+If {kib} terminates unexpectedly while migrating a saved object index it will automatically attempt to perform the migration again once the process has restarted. Do not delete any saved objects indices to attempt to fix a failed migration. Unlike previous versions, {kib} version 7.12.0 and later does not require deleting any indices to release a failed migration lock.
-1. Shutdown all {kib} instances to be 100% sure that there are no instances currently performing a migration.
-2. Identify any migration locks by comparing the output of `GET /_cat/aliases` and `GET /_cat/indices`. If e.g. `.kibana` is pointing to `.kibana_4` and there is a `.kibana_5` index, the `.kibana_5` index will act like a migration lock blocking further attempts. Be sure to check both the `.kibana` and `.kibana_task_manager` aliases and their indices.
-3. Remove any migration locks e.g. `DELETE /.kibana_5`.
-4. Start up all {kib} instances.
+If upgrade migrations fail repeatedly, follow the advice in (preventing migration failures)[preventing-migration-failures]. Once the root cause for the migration failure has been addressed, {kib} will automatically retry the migration without any further intervention. If you're unable to resolve a failed migration following these steps, please contact support.
[float]
[[upgrade-migrations-rolling-back]]
==== Rolling back to a previous version of {kib}
-If you've followed the advice in (preventing migration failures)[preventing-migration-failures] and (resolving migration failures)[resolve-migrations-failures] and {kib} is still not able to upgrade successfully, you might choose to rollback {kib} until you're able to identify the root cause.
+If you've followed the advice in (preventing migration failures)[preventing-migration-failures] and (resolving migration failures)[resolve-migrations-failures] and {kib} is still not able to upgrade successfully, you might choose to rollback {kib} until you're able to identify and fix the root cause.
WARNING: Before rolling back {kib}, ensure that the version you wish to rollback to is compatible with your {es} cluster. If the version you're rolling back to is not compatible, you will have to also rollback {es}. +
Any changes made after an upgrade will be lost when rolling back to a previous version.
-In order to rollback after a failed upgrade migration, the saved object indices might also have to be rolled back to be compatible with the previous {kibana} version.
+In order to rollback after a failed upgrade migration, the saved object indices have to be rolled back to be compatible with the previous {kibana} version.
[float]
===== Rollback by restoring a backup snapshot:
@@ -111,17 +110,15 @@ In order to rollback after a failed upgrade migration, the saved object indices
[float]
===== (Not recommended) Rollback without a backup snapshot:
-WARNING: {kib} does not run a migration for every saved object index on every upgrade. A {kib} version upgrade can cause no migrations, migrate only the `.kibana` or the `.kibana_task_manager` index or both. Carefully read the logs to ensure that you're only deleting indices created by a later version of {kib} to avoid data loss.
-
1. Shutdown all {kib} instances to be 100% sure that there are no {kib} instances currently performing a migration.
2. Create a backup snapshot of the `.kibana*` indices.
-3. Use the logs from the upgraded instances to identify which indices {kib} attempted to upgrade. The server logs will contain an entry like `[savedobjects-service] Creating index .kibana_4.` and/or `[savedobjects-service] Creating index .kibana_task_manager_2.` If no indices were created after upgrading {kib} then no further action is required to perform a rollback, skip ahead to step (5). If you're running multiple {kib} instances, be sure to inspect all instances' logs.
-4. Delete each of the indices identified in step (2). e.g. `DELETE /.kibana_task_manager_2`
-5. Inspect the output of `GET /_cat/aliases`. If either the `.kibana` and/or `.kibana_task_manager` alias is missing, these will have to be created manually. Find the latest index from the output of `GET /_cat/indices` and create the missing alias to point to the latest index. E.g. if the `.kibana` alias was missing and the latest index is `.kibana_3` create a new alias with `POST /.kibana_3/_aliases/.kibana`.
+3. Delete the version specific indices created by the failed upgrade migration. E.g. if you wish to rollback from a failed upgrade to v7.12.0 `DELETE /.kibana_7.12.0_*,.kibana_task_manager_7.12.0_*`
+4. Inspect the output of `GET /_cat/aliases`. If either the `.kibana` and/or `.kibana_task_manager` alias is missing, these will have to be created manually. Find the latest index from the output of `GET /_cat/indices` and create the missing alias to point to the latest index. E.g. if the `.kibana` alias was missing and the latest index is `.kibana_3` create a new alias with `POST /.kibana_3/_aliases/.kibana`.
+5. Remove the write block from the rollback indices. `PUT /.kibana,.kibana_task_manager/_settings {"index.blocks.write": false}`
6. Start up {kib} on the older version you wish to rollback to.
[float]
[[upgrade-migrations-old-indices]]
==== Handling old `.kibana_N` indices
-After migrations have completed, there will be multiple {kib} indices in {es}: (`.kibana_1`, `.kibana_2`, etc). {kib} only uses the index that the `.kibana` alias points to. The other {kib} indices can be safely deleted, but are left around as a matter of historical record, and to facilitate rolling {kib} back to a previous version.
\ No newline at end of file
+After migrations have completed, there will be multiple {kib} indices in {es}: (`.kibana_1`, `.kibana_2`, `.kibana_7.12.0` etc). {kib} only uses the index that the `.kibana` and `.kibana_task_manager` alias points to. The other {kib} indices can be safely deleted, but are left around as a matter of historical record, and to facilitate rolling {kib} back to a previous version.
\ No newline at end of file
diff --git a/docs/setup/upgrade/upgrade-standard.asciidoc b/docs/setup/upgrade/upgrade-standard.asciidoc
index b27bb8867e624..b43da6aef9765 100644
--- a/docs/setup/upgrade/upgrade-standard.asciidoc
+++ b/docs/setup/upgrade/upgrade-standard.asciidoc
@@ -15,17 +15,17 @@ necessary remediation steps as per those instructions.
[float]
==== Upgrading multiple {kib} instances
-WARNING: Kibana does not support rolling upgrades. If you're running multiple {kib} instances, all instances should be stopped before upgrading.
+NOTE: Kibana does not support rolling upgrades. If you're running multiple {kib} instances, all instances should be stopped before upgrading.
-Different versions of {kib} running against the same {es} index, such as during a rolling upgrade, can cause upgrade migration failures and data loss. This is because acknowledged writes from the older instances could be written into the _old_ index while the migration is in progress. To prevent this from happening ensure that all old {kib} instances are shutdown before starting up instances on a newer version.
-
-The first instance that triggers saved object migrations will run the entire process. Any other instances started up while a migration is running will log a message and then wait until saved object migrations has completed before they start serving HTTP traffic.
+Different versions of {kib} running against the same {es} index, such as during a rolling upgrade, can cause data loss. This is because older instances will continue to write saved objects in a different format than the newer instances. To prevent this from happening ensure that all old {kib} instances are shutdown before starting up instances on a newer version.
[float]
==== Upgrade using a `deb` or `rpm` package
. Stop the existing {kib} process using the appropriate command for your
- system. If you have multiple {kib} instances connecting to the same {es} cluster ensure that all instances are stopped before proceeding to the next step to avoid data loss.
+ system. If you have multiple {kib} instances connecting to the same {es}
+ cluster ensure that all instances are stopped before proceeding to the next
+ step to avoid data loss.
. Use `rpm` or `dpkg` to install the new package. All files should be placed in
their proper locations and config files should not be overwritten.
+
@@ -65,5 +65,7 @@ and becomes a new instance in the monitoring data.
. Install the appropriate versions of all your plugins for your new
installation using the `kibana-plugin` script. Check out the
<> documentation for more information.
-. Stop the old {kib} process. If you have multiple {kib} instances connecting to the same {es} cluster ensure that all instances are stopped before proceeding to the next step to avoid data loss.
+. Stop the old {kib} process. If you have multiple {kib} instances connecting
+ to the same {es} cluster ensure that all instances are stopped before
+ proceeding to the next step to avoid data loss.
. Start the new {kib} process.
diff --git a/examples/expressions_explorer/README.md b/examples/expressions_explorer/README.md
new file mode 100644
index 0000000000000..ead0ca758f8e5
--- /dev/null
+++ b/examples/expressions_explorer/README.md
@@ -0,0 +1,8 @@
+## expressions explorer
+
+This example expressions explorer app shows how to:
+ - to run expression
+ - to render expression output
+ - emit events from expression renderer and handle them
+
+To run this example, use the command `yarn start --run-examples`.
\ No newline at end of file
diff --git a/examples/expressions_explorer/kibana.json b/examples/expressions_explorer/kibana.json
new file mode 100644
index 0000000000000..038b7eea0ef21
--- /dev/null
+++ b/examples/expressions_explorer/kibana.json
@@ -0,0 +1,10 @@
+{
+ "id": "expressionsExplorer",
+ "version": "0.0.1",
+ "kibanaVersion": "kibana",
+ "server": false,
+ "ui": true,
+ "requiredPlugins": ["expressions", "inspector", "uiActions", "developerExamples"],
+ "optionalPlugins": [],
+ "requiredBundles": []
+}
diff --git a/examples/expressions_explorer/public/actions/navigate_action.ts b/examples/expressions_explorer/public/actions/navigate_action.ts
new file mode 100644
index 0000000000000..d29a9e6b345b6
--- /dev/null
+++ b/examples/expressions_explorer/public/actions/navigate_action.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 { createAction } from '../../../../src/plugins/ui_actions/public';
+
+export const ACTION_NAVIGATE = 'ACTION_NAVIGATE';
+
+export const createNavigateAction = () =>
+ createAction({
+ id: ACTION_NAVIGATE,
+ type: ACTION_NAVIGATE,
+ getDisplayName: () => 'Navigate',
+ execute: async (event: any) => {
+ window.location.href = event.href;
+ },
+ });
diff --git a/examples/expressions_explorer/public/actions/navigate_trigger.ts b/examples/expressions_explorer/public/actions/navigate_trigger.ts
new file mode 100644
index 0000000000000..eacbd968eaa93
--- /dev/null
+++ b/examples/expressions_explorer/public/actions/navigate_trigger.ts
@@ -0,0 +1,15 @@
+/*
+ * 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 { Trigger } from '../../../../src/plugins/ui_actions/public';
+
+export const NAVIGATE_TRIGGER_ID = 'NAVIGATE_TRIGGER_ID';
+
+export const navigateTrigger: Trigger = {
+ id: NAVIGATE_TRIGGER_ID,
+};
diff --git a/examples/expressions_explorer/public/actions_and_expressions.tsx b/examples/expressions_explorer/public/actions_and_expressions.tsx
new file mode 100644
index 0000000000000..6e2eebcde4a0f
--- /dev/null
+++ b/examples/expressions_explorer/public/actions_and_expressions.tsx
@@ -0,0 +1,102 @@
+/*
+ * 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, { useState } from 'react';
+import {
+ EuiFlexItem,
+ EuiFlexGroup,
+ EuiPageBody,
+ EuiPageContent,
+ EuiPageContentBody,
+ EuiPageHeader,
+ EuiPageHeaderSection,
+ EuiPanel,
+ EuiText,
+ EuiTitle,
+} from '@elastic/eui';
+import {
+ ExpressionsStart,
+ ReactExpressionRenderer,
+ ExpressionsInspectorAdapter,
+} from '../../../src/plugins/expressions/public';
+import { ExpressionEditor } from './editor/expression_editor';
+import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
+import { NAVIGATE_TRIGGER_ID } from './actions/navigate_trigger';
+
+interface Props {
+ expressions: ExpressionsStart;
+ actions: UiActionsStart;
+}
+
+export function ActionsExpressionsExample({ expressions, actions }: Props) {
+ const [expression, updateExpression] = useState(
+ 'button name="click me" href="http://www.google.com"'
+ );
+
+ const expressionChanged = (value: string) => {
+ updateExpression(value);
+ };
+
+ const inspectorAdapters = {
+ expression: new ExpressionsInspectorAdapter(),
+ };
+
+ const handleEvents = (event: any) => {
+ if (event.id !== 'NAVIGATE') return;
+ // enrich event context with some extra data
+ event.baseUrl = 'http://www.google.com';
+
+ actions.executeTriggerActions(NAVIGATE_TRIGGER_ID, event.value);
+ };
+
+ return (
+
+
+
+
+
Actions from expression renderers
+
+
+
+
+
+
+
+
+ Here you can play with sample `button` which takes a url as configuration and
+ displays a button which emits custom BUTTON_CLICK trigger to which we have attached
+ a custom action which performs the navigation.
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ return
{message}
;
+ }}
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/examples/expressions_explorer/public/app.tsx b/examples/expressions_explorer/public/app.tsx
new file mode 100644
index 0000000000000..d72cf08128a5a
--- /dev/null
+++ b/examples/expressions_explorer/public/app.tsx
@@ -0,0 +1,78 @@
+/*
+ * 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 ReactDOM from 'react-dom';
+import {
+ EuiPage,
+ EuiPageHeader,
+ EuiPageBody,
+ EuiPageContent,
+ EuiPageContentBody,
+ EuiSpacer,
+ EuiText,
+ EuiLink,
+} from '@elastic/eui';
+import { AppMountParameters } from '../../../src/core/public';
+import { ExpressionsStart } from '../../../src/plugins/expressions/public';
+import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
+import { RunExpressionsExample } from './run_expressions';
+import { RenderExpressionsExample } from './render_expressions';
+import { ActionsExpressionsExample } from './actions_and_expressions';
+import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
+
+interface Props {
+ expressions: ExpressionsStart;
+ inspector: InspectorStart;
+ actions: UiActionsStart;
+}
+
+const ExpressionsExplorer = ({ expressions, inspector, actions }: Props) => {
+ return (
+
+
+ Expressions Explorer
+
+
+
+
+ There are a couple of ways to run the expressions. Below some of the options are
+ demonstrated. You can read more about it{' '}
+
+ here
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const renderApp = (props: Props, { element }: AppMountParameters) => {
+ ReactDOM.render(, element);
+
+ return () => ReactDOM.unmountComponentAtNode(element);
+};
diff --git a/examples/expressions_explorer/public/editor/expression_editor.tsx b/examples/expressions_explorer/public/editor/expression_editor.tsx
new file mode 100644
index 0000000000000..e3dbb5998b92e
--- /dev/null
+++ b/examples/expressions_explorer/public/editor/expression_editor.tsx
@@ -0,0 +1,35 @@
+/*
+ * 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 { EuiCodeEditor } from '@elastic/eui';
+
+interface Props {
+ value: string;
+ onChange: (value: string) => void;
+}
+
+export function ExpressionEditor({ value, onChange }: Props) {
+ return (
+ {}}
+ aria-label="Code Editor"
+ />
+ );
+}
diff --git a/examples/expressions_explorer/public/functions/button.ts b/examples/expressions_explorer/public/functions/button.ts
new file mode 100644
index 0000000000000..8c39aa2743b30
--- /dev/null
+++ b/examples/expressions_explorer/public/functions/button.ts
@@ -0,0 +1,50 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 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 { i18n } from '@kbn/i18n';
+import { ExpressionFunctionDefinition } from '../../../../src/plugins/expressions/common';
+
+interface Arguments {
+ href: string;
+ name: string;
+}
+
+export type ExpressionFunctionButton = ExpressionFunctionDefinition<
+ 'button',
+ unknown,
+ Arguments,
+ unknown
+>;
+
+export const buttonFn: ExpressionFunctionButton = {
+ name: 'button',
+ args: {
+ href: {
+ help: i18n.translate('expressions.functions.font.args.href', {
+ defaultMessage: 'Link to which to navigate',
+ }),
+ types: ['string'],
+ required: true,
+ },
+ name: {
+ help: i18n.translate('expressions.functions.font.args.name', {
+ defaultMessage: 'Name of the button',
+ }),
+ types: ['string'],
+ default: 'button',
+ },
+ },
+ help: 'Configures the button',
+ fn: (input: unknown, args: Arguments) => {
+ return {
+ type: 'render',
+ as: 'button',
+ value: args,
+ };
+ },
+};
diff --git a/examples/expressions_explorer/public/index.ts b/examples/expressions_explorer/public/index.ts
new file mode 100644
index 0000000000000..a6dbbc9198f44
--- /dev/null
+++ b/examples/expressions_explorer/public/index.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 { ExpressionsExplorerPlugin } from './plugin';
+
+export const plugin = () => new ExpressionsExplorerPlugin();
diff --git a/examples/expressions_explorer/public/inspector/ast_debug_view.tsx b/examples/expressions_explorer/public/inspector/ast_debug_view.tsx
new file mode 100644
index 0000000000000..d860ff30bd8e9
--- /dev/null
+++ b/examples/expressions_explorer/public/inspector/ast_debug_view.tsx
@@ -0,0 +1,78 @@
+/*
+ * 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, { useState } from 'react';
+import { EuiTreeView, EuiDescriptionList, EuiCodeBlock, EuiText, EuiSpacer } from '@elastic/eui';
+
+interface Props {
+ ast: any;
+}
+
+const decorateAst = (ast: any, nodeClicked: any) => {
+ return ast.chain.map((link: any) => {
+ return {
+ id: link.function + Math.random(),
+ label: link.function,
+ callback: () => {
+ nodeClicked(link.debug);
+ },
+ children: Object.keys(link.arguments).reduce((result: any, key: string) => {
+ if (typeof link.arguments[key] === 'object') {
+ // result[key] = decorateAst(link.arguments[key]);
+ }
+ return result;
+ }, []),
+ };
+ });
+};
+
+const prepareNode = (key: string, value: any) => {
+ if (key === 'args') {
+ return (
+
+ {JSON.stringify(value, null, '\t')}
+
+ );
+ } else if (key === 'output' || key === 'input') {
+ return (
+
+ {JSON.stringify(value, null, '\t')}
+
+ );
+ } else if (key === 'success') {
+ return value ? 'true' : 'false';
+ } else return {value};
+};
+
+export function AstDebugView({ ast }: Props) {
+ const [nodeInfo, setNodeInfo] = useState([] as any[]);
+ const items = decorateAst(ast, (node: any) => {
+ setNodeInfo(
+ Object.keys(node).map((key) => ({
+ title: key,
+ description: prepareNode(key, node[key]),
+ }))
+ );
+ });
+
+ return (
+
+ List of executed expression functions:
+
+
+ Details of selected function:
+
+
+ );
+}
diff --git a/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx b/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx
new file mode 100644
index 0000000000000..1233735072d04
--- /dev/null
+++ b/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx
@@ -0,0 +1,98 @@
+/*
+ * 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, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiEmptyPrompt } from '@elastic/eui';
+import { InspectorViewProps, Adapters } from '../../../../src/plugins/inspector/public';
+import { AstDebugView } from './ast_debug_view';
+
+interface ExpressionsInspectorViewComponentState {
+ ast: any;
+ adapters: Adapters;
+}
+
+class ExpressionsInspectorViewComponent extends Component<
+ InspectorViewProps,
+ ExpressionsInspectorViewComponentState
+> {
+ static propTypes = {
+ adapters: PropTypes.object.isRequired,
+ title: PropTypes.string.isRequired,
+ };
+
+ state = {} as ExpressionsInspectorViewComponentState;
+
+ static getDerivedStateFromProps(
+ nextProps: Readonly,
+ state: ExpressionsInspectorViewComponentState
+ ) {
+ if (state && nextProps.adapters === state.adapters) {
+ return null;
+ }
+
+ const { ast } = nextProps.adapters.expression;
+
+ return {
+ adapters: nextProps.adapters,
+ ast,
+ };
+ }
+
+ onUpdateData = (ast: any) => {
+ this.setState({
+ ast,
+ });
+ };
+
+ componentDidMount() {
+ this.props.adapters.expression!.on('change', this.onUpdateData);
+ }
+
+ componentWillUnmount() {
+ this.props.adapters.expression!.removeListener('change', this.onUpdateData);
+ }
+
+ static renderNoData() {
+ return (
+
+
+
+ }
+ body={
+
+
+
+
+
+ }
+ />
+ );
+ }
+
+ render() {
+ if (!this.state.ast) {
+ return ExpressionsInspectorViewComponent.renderNoData();
+ }
+
+ return ;
+ }
+}
+
+// default export required for React.Lazy
+// eslint-disable-next-line import/no-default-export
+export default ExpressionsInspectorViewComponent;
diff --git a/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx b/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx
new file mode 100644
index 0000000000000..b10c82e5df309
--- /dev/null
+++ b/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx
@@ -0,0 +1,17 @@
+/*
+ * 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, { lazy } from 'react';
+
+const ExpressionsInspectorViewComponent = lazy(() => import('./expressions_inspector_view'));
+
+export const getExpressionsInspectorViewComponentWrapper = () => {
+ return (props: any) => {
+ return ;
+ };
+};
diff --git a/examples/expressions_explorer/public/inspector/index.ts b/examples/expressions_explorer/public/inspector/index.ts
new file mode 100644
index 0000000000000..ec87a1240ac74
--- /dev/null
+++ b/examples/expressions_explorer/public/inspector/index.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { Adapters, InspectorViewDescription } from '../../../../src/plugins/inspector/public';
+import { getExpressionsInspectorViewComponentWrapper } from './expressions_inspector_view_wrapper';
+
+export const getExpressionsInspectorViewDescription = (): InspectorViewDescription => ({
+ title: i18n.translate('data.inspector.table.dataTitle', {
+ defaultMessage: 'Expression',
+ }),
+ order: 100,
+ help: i18n.translate('data.inspector.table..dataDescriptionTooltip', {
+ defaultMessage: 'View the expression behind the visualization',
+ }),
+ shouldShow(adapters: Adapters) {
+ return Boolean(adapters.expression);
+ },
+ component: getExpressionsInspectorViewComponentWrapper(),
+});
diff --git a/examples/expressions_explorer/public/plugin.tsx b/examples/expressions_explorer/public/plugin.tsx
new file mode 100644
index 0000000000000..9643389ad881c
--- /dev/null
+++ b/examples/expressions_explorer/public/plugin.tsx
@@ -0,0 +1,87 @@
+/*
+ * 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 { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public';
+import { DeveloperExamplesSetup } from '../../developer_examples/public';
+import { ExpressionsSetup, ExpressionsStart } from '../../../src/plugins/expressions/public';
+import {
+ Setup as InspectorSetup,
+ Start as InspectorStart,
+} from '../../../src/plugins/inspector/public';
+import { getExpressionsInspectorViewDescription } from './inspector';
+import { UiActionsStart, UiActionsSetup } from '../../../src/plugins/ui_actions/public';
+import { NAVIGATE_TRIGGER_ID, navigateTrigger } from './actions/navigate_trigger';
+import { ACTION_NAVIGATE, createNavigateAction } from './actions/navigate_action';
+import { buttonRenderer } from './renderers/button';
+import { buttonFn } from './functions/button';
+
+interface StartDeps {
+ expressions: ExpressionsStart;
+ inspector: InspectorStart;
+ uiActions: UiActionsStart;
+}
+
+interface SetupDeps {
+ uiActions: UiActionsSetup;
+ expressions: ExpressionsSetup;
+ inspector: InspectorSetup;
+ developerExamples: DeveloperExamplesSetup;
+}
+
+export class ExpressionsExplorerPlugin implements Plugin {
+ public setup(core: CoreSetup, deps: SetupDeps) {
+ // register custom inspector adapter & view
+ deps.inspector.registerView(getExpressionsInspectorViewDescription());
+
+ // register custom actions
+ deps.uiActions.registerTrigger(navigateTrigger);
+ deps.uiActions.registerAction(createNavigateAction());
+ deps.uiActions.attachAction(NAVIGATE_TRIGGER_ID, ACTION_NAVIGATE);
+
+ // register custom functions and renderers
+ deps.expressions.registerRenderer(buttonRenderer);
+ deps.expressions.registerFunction(buttonFn);
+
+ core.application.register({
+ id: 'expressionsExplorer',
+ title: 'Expressions Explorer',
+ navLinkStatus: AppNavLinkStatus.hidden,
+ async mount(params: AppMountParameters) {
+ const [, depsStart] = await core.getStartServices();
+ const { renderApp } = await import('./app');
+ return renderApp(
+ {
+ expressions: depsStart.expressions,
+ inspector: depsStart.inspector,
+ actions: depsStart.uiActions,
+ },
+ params
+ );
+ },
+ });
+
+ deps.developerExamples.register({
+ appId: 'expressionsExplorer',
+ title: 'Expressions',
+ description: `Expressions is a plugin that allows to execute Kibana expressions and render content using expression renderers. This example plugin showcases various usage scenarios.`,
+ links: [
+ {
+ label: 'README',
+ href: 'https://github.com/elastic/kibana/blob/master/src/plugins/expressions/README.md',
+ iconType: 'logoGithub',
+ size: 's',
+ target: '_blank',
+ },
+ ],
+ });
+ }
+
+ public start() {}
+
+ public stop() {}
+}
diff --git a/examples/expressions_explorer/public/render_expressions.tsx b/examples/expressions_explorer/public/render_expressions.tsx
new file mode 100644
index 0000000000000..ffbe558f30218
--- /dev/null
+++ b/examples/expressions_explorer/public/render_expressions.tsx
@@ -0,0 +1,99 @@
+/*
+ * 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, { useState } from 'react';
+import {
+ EuiFlexItem,
+ EuiFlexGroup,
+ EuiPageBody,
+ EuiPageContent,
+ EuiPageContentBody,
+ EuiPageHeader,
+ EuiPageHeaderSection,
+ EuiPanel,
+ EuiText,
+ EuiTitle,
+ EuiButton,
+} from '@elastic/eui';
+import {
+ ExpressionsStart,
+ ReactExpressionRenderer,
+ ExpressionsInspectorAdapter,
+} from '../../../src/plugins/expressions/public';
+import { ExpressionEditor } from './editor/expression_editor';
+import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
+
+interface Props {
+ expressions: ExpressionsStart;
+ inspector: InspectorStart;
+}
+
+export function RenderExpressionsExample({ expressions, inspector }: Props) {
+ const [expression, updateExpression] = useState('markdown "## expressions explorer rendering"');
+
+ const expressionChanged = (value: string) => {
+ updateExpression(value);
+ };
+
+ const inspectorAdapters = {
+ expression: new ExpressionsInspectorAdapter(),
+ };
+
+ return (
+
+
+
+
+
Render expressions
+
+
+
+
+
+
+
+
+ In the below editor you can enter your expression and render it. Using
+ ReactExpressionRenderer component makes that very easy.
+
+
+
+ {
+ inspector.open(inspectorAdapters);
+ }}
+ >
+ Open Inspector
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ return
{message}
;
+ }}
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/examples/expressions_explorer/public/renderers/button.tsx b/examples/expressions_explorer/public/renderers/button.tsx
new file mode 100644
index 0000000000000..32f1f31894dce
--- /dev/null
+++ b/examples/expressions_explorer/public/renderers/button.tsx
@@ -0,0 +1,40 @@
+/*
+ * 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 ReactDOM from 'react-dom';
+import React from 'react';
+import { EuiButton } from '@elastic/eui';
+import { ExpressionRenderDefinition } from '../../../../src/plugins/expressions/common/expression_renderers';
+
+export const buttonRenderer: ExpressionRenderDefinition = {
+ name: 'button',
+ displayName: 'Button',
+ reuseDomNode: true,
+ render(domNode, config, handlers) {
+ const buttonClick = () => {
+ handlers.event({
+ id: 'NAVIGATE',
+ value: {
+ href: config.href,
+ },
+ });
+ };
+
+ const renderDebug = () => (
+
+
+ {config.name}
+
+
+ );
+
+ ReactDOM.render(renderDebug(), domNode, () => handlers.done());
+
+ handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
+ },
+};
diff --git a/examples/expressions_explorer/public/run_expressions.tsx b/examples/expressions_explorer/public/run_expressions.tsx
new file mode 100644
index 0000000000000..efbdbc2d41836
--- /dev/null
+++ b/examples/expressions_explorer/public/run_expressions.tsx
@@ -0,0 +1,118 @@
+/*
+ * 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, { useState, useEffect, useMemo } from 'react';
+import {
+ EuiCodeBlock,
+ EuiFlexItem,
+ EuiFlexGroup,
+ EuiPageBody,
+ EuiPageContent,
+ EuiPageContentBody,
+ EuiPageHeader,
+ EuiPageHeaderSection,
+ EuiPanel,
+ EuiText,
+ EuiTitle,
+ EuiButton,
+} from '@elastic/eui';
+import {
+ ExpressionsStart,
+ ExpressionsInspectorAdapter,
+} from '../../../src/plugins/expressions/public';
+import { ExpressionEditor } from './editor/expression_editor';
+import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
+
+interface Props {
+ expressions: ExpressionsStart;
+ inspector: InspectorStart;
+}
+
+export function RunExpressionsExample({ expressions, inspector }: Props) {
+ const [expression, updateExpression] = useState('markdown "## expressions explorer"');
+ const [result, updateResult] = useState({});
+
+ const expressionChanged = (value: string) => {
+ updateExpression(value);
+ };
+
+ const inspectorAdapters = useMemo(
+ () => ({
+ expression: new ExpressionsInspectorAdapter(),
+ }),
+ []
+ );
+
+ useEffect(() => {
+ const runExpression = async () => {
+ const execution = expressions.execute(expression, null, {
+ debug: true,
+ inspectorAdapters,
+ });
+
+ const data: any = await execution.getData();
+ updateResult(data);
+ };
+
+ runExpression();
+ }, [expression, expressions, inspectorAdapters]);
+
+ return (
+
+
+
+
+
Run expressions
+
+
+
+
+
+
+
+
+ In the below editor you can enter your expression and execute it. Using
+ expressions.execute allows you to easily run the expression.
+
+
+
+ {
+ inspector.open(inspectorAdapters);
+ }}
+ >
+ Open Inspector
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {JSON.stringify(result, null, '\t')}
+
+
+
+
+
+
+
+ );
+}
diff --git a/examples/expressions_explorer/tsconfig.json b/examples/expressions_explorer/tsconfig.json
new file mode 100644
index 0000000000000..b4449819b25a6
--- /dev/null
+++ b/examples/expressions_explorer/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "./target",
+ "skipLibCheck": true
+ },
+ "include": [
+ "index.ts",
+ "public/**/*.ts",
+ "public/**/*.tsx",
+ "../../typings/**/*",
+ ],
+ "exclude": [],
+ "references": [
+ { "path": "../../src/core/tsconfig.json" },
+ { "path": "../../src/plugins/kibana_react/tsconfig.json" },
+ ]
+}
diff --git a/package.json b/package.json
index 87e0f84695235..dac83dacf6fbf 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",
@@ -828,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 4681057ad0998..f23d8c9936980 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 {
);
});
+ 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 e4b89a949d3cf..e93586ec7ce4c 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/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts
index 20865bea2f897..7c28db333cc83 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 0000000000000..56db5346b7c6c
--- /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 60a0c56a63218..fb57f8caa5ce4 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/public/ui/query_string_input/_query_bar.scss b/src/plugins/data/public/ui/query_string_input/_query_bar.scss
index 3e3982dd58e57..65f652df31d0c 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/discover/public/application/angular/directives/histogram.tsx b/src/plugins/discover/public/application/angular/directives/histogram.tsx
index ff10feea46d47..b12de3f4496c5 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 45e5e252e8361..809664de5f073 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 fe05fceb858e5..c769e263655ab 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 db58aebfd3ad7..89317f77ef060 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/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 b7f7a5abb82b0..e2e1d7f05851c 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 9207c6467f6a9..151bd91750daa 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 8e068818ec0ce..0240ec90cb1e6 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/src/plugins/expressions/common/util/expressions_inspector_adapter.ts b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts
new file mode 100644
index 0000000000000..c82884d373d2f
--- /dev/null
+++ b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts
@@ -0,0 +1,22 @@
+/*
+ * 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 { EventEmitter } from 'events';
+
+export class ExpressionsInspectorAdapter extends EventEmitter {
+ private _ast: any = {};
+
+ public logAST(ast: any): void {
+ this._ast = ast;
+ this.emit('change', this._ast);
+ }
+
+ 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 ecb7d5cdca81e..4762f9979fe4a 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 9485daf49c981..d6dd2fc1f3d37 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 7fa0857be8aba..029d727e82e74 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 1484ce6b1a81f..d38c77faab7f8 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/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 aab16f9d79c4b..fe002500309ad 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/common/index.ts b/src/plugins/kibana_react/common/index.ts
new file mode 100644
index 0000000000000..0af8ed573699b
--- /dev/null
+++ b/src/plugins/kibana_react/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 './eui_styled_components';
diff --git a/src/plugins/kibana_react/kibana.json b/src/plugins/kibana_react/kibana.json
index f2f0da53e6280..6bf7ff1d82070 100644
--- a/src/plugins/kibana_react/kibana.json
+++ b/src/plugins/kibana_react/kibana.json
@@ -2,5 +2,6 @@
"id": "kibanaReact",
"version": "kibana",
"ui": true,
- "server": false
+ "server": false,
+ "extraPublicDirs": ["common"]
}
diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts
index c99da5e9b36b8..cbb60bba47861 100644
--- a/src/plugins/kibana_react/public/index.ts
+++ b/src/plugins/kibana_react/public/index.ts
@@ -15,6 +15,7 @@ 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';
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 0000000000000..294be46398e8a
--- /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 0000000000000..f952741291b68
--- /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 61b02f47678c3..f290b3c7c5f89 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 0000000000000..3d4ce29ffa5e9
--- /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 2ba227e6ff84f..388a11992268e 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/tsconfig.json b/src/plugins/kibana_react/tsconfig.json
index 52899f868dbfb..eb9a24ca141f6 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/saved_objects_management/public/lib/create_field_list.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.ts
index cd30a02bd0ef3..4497fb04ffa2c 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 96a4a24f6591e..e048b92b9566c 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 31c0a76e16f58..3343e0a63f54c 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 758789aa0f47e..2af7c22488c51 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 fc3fa3e72b9c0..95d3026f66d37 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 50a08d96de951..27d9b5ce83203 100644
--- a/src/plugins/telemetry/schema/oss_plugins.json
+++ b/src/plugins/telemetry/schema/oss_plugins.json
@@ -5247,6 +5247,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/vis_type_table/common/index.ts b/src/plugins/vis_type_table/common/index.ts
new file mode 100644
index 0000000000000..cc54db82d37e7
--- /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 0000000000000..3380e730770c3
--- /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 4e5ddbcf8d7c5..3a7906f6ec543 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 eb76659a601d6..a70ecb43f1be7 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
@@ -19,7 +19,7 @@ import {
NumberInputOption,
VisOptionsProps,
} from '../../../vis_default_editor/public';
-import { TableVisParams } from '../types';
+import { TableVisParams } from '../../common';
import { totalAggregations } from './utils';
const { tabifyGetColumns } = search;
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 fb0044a986f5e..716b77e9c91d2 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
@@ -9,7 +9,7 @@
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 { TableVisParams } from '../../common';
const TableOptionsComponent = lazy(() => import('./table_vis_options'));
diff --git a/src/plugins/vis_type_table/public/components/utils.ts b/src/plugins/vis_type_table/public/components/utils.ts
index f11d7bc4b7f33..8f30788c76468 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 cec16eefb360c..db0b92154d2dd 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 a1ceee8c741d4..3e1140275593d 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
@@ -12,11 +12,11 @@ import { BaseVisTypeOptions } 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',
+ name: VIS_TYPE_TABLE,
title: i18n.translate('visTypeTable.tableVisTitle', {
defaultMessage: 'Data table',
}),
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 a45f1e828fc47..99fee424b8bea 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 8cd45b54c6ced..ef6d85db103b3 100644
--- a/src/plugins/vis_type_table/public/table_vis_type.ts
+++ b/src/plugins/vis_type_table/public/table_vis_type.ts
@@ -10,13 +10,13 @@ import { i18n } from '@kbn/i18n';
import { AggGroupNames } from '../../data/public';
import { BaseVisTypeOptions } 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',
+ name: VIS_TYPE_TABLE,
title: i18n.translate('visTypeTable.tableVisTitle', {
defaultMessage: 'Data table',
}),
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 1ca62475b7af0..f0aed7199a2f2 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 9d9f23d31d802..1cbe9832e4c98 100644
--- a/src/plugins/vis_type_table/public/to_ast.ts
+++ b/src/plugins/vis_type_table/public/to_ast.ts
@@ -12,8 +12,9 @@ import {
} from '../../data/public';
import { buildExpression, buildExpressionFunction } from '../../expressions/public';
import { getVisSchemas, Vis, BuildPipelineParams } 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,
diff --git a/src/plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts
index 75d48f4f53ac7..03cf8bb3395d6 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 5398aa908f6eb..3a733e7a9a4dc 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 1573a3c6b7b88..7e55e63f9249c 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 75068c646f501..39618d687168e 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 0000000000000..55daa5c64349a
--- /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 0000000000000..bd3e1d2f089e2
--- /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 0000000000000..090ed3077b27c
--- /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 0000000000000..cbf39a4d937a7
--- /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 0000000000000..2ac4ce22a47e4
--- /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 bda86d06c0ff7..ccff3c349cf21 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_vislib/public/vislib/lib/axis/axis_scale.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js
index 157523cdf09f4..ee9bed141fe4b 100644
--- a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js
+++ b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js
@@ -7,7 +7,7 @@
*/
import d3 from 'd3';
-import _ from 'lodash';
+import { isNumber, reduce, times } from 'lodash';
import moment from 'moment';
import { InvalidLogScaleValues } from '../../errors';
@@ -62,7 +62,7 @@ export class AxisScale {
return d3[extent](
opts.reduce(function (opts, v) {
- if (!_.isNumber(v)) v = +v;
+ if (!isNumber(v)) v = +v;
if (!isNaN(v)) opts.push(v);
return opts;
}, [])
@@ -90,7 +90,7 @@ export class AxisScale {
const y = moment(x);
const method = n > 0 ? 'add' : 'subtract';
- _.times(Math.abs(n), function () {
+ times(Math.abs(n), function () {
y[method](interval);
});
@@ -100,7 +100,7 @@ export class AxisScale {
getAllPoints() {
const config = this.axisConfig;
const data = this.visConfig.data.chartData();
- const chartPoints = _.reduce(
+ const chartPoints = reduce(
data,
(chartPoints, chart, chartIndex) => {
const points = chart.series.reduce((points, seri, seriIndex) => {
@@ -254,6 +254,6 @@ export class AxisScale {
}
validateScale(scale) {
- if (!scale || _.isNaN(scale)) throw new Error('scale is ' + scale);
+ if (!scale || Number.isNaN(scale)) throw new Error('scale is ' + scale);
}
}
diff --git a/src/plugins/vis_type_xy/public/chart_splitter.tsx b/src/plugins/vis_type_xy/public/chart_splitter.tsx
new file mode 100644
index 0000000000000..bf63ac1896bd1
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/chart_splitter.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 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 { Accessor, AccessorFn, GroupBy, GroupBySort, SmallMultiples } from '@elastic/charts';
+
+interface ChartSplitterProps {
+ splitColumnAccessor?: Accessor | AccessorFn;
+ splitRowAccessor?: Accessor | AccessorFn;
+ sort?: GroupBySort;
+}
+
+const CHART_SPLITTER_ID = '__chart_splitter__';
+
+export const ChartSplitter = ({
+ splitColumnAccessor,
+ splitRowAccessor,
+ sort,
+}: ChartSplitterProps) =>
+ splitColumnAccessor || splitRowAccessor ? (
+ <>
+ {
+ const splitTypeAccessor = splitColumnAccessor || splitRowAccessor;
+ if (splitTypeAccessor) {
+ return typeof splitTypeAccessor === 'function'
+ ? splitTypeAccessor(datum)
+ : datum[splitTypeAccessor];
+ }
+ return spec.id;
+ }}
+ sort={sort || 'dataIndex'}
+ />
+
+ >
+ ) : null;
diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
index 49b2ab483bc55..02c7157d32c27 100644
--- a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
+++ b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
@@ -16,19 +16,20 @@ import {
XYChartSeriesIdentifier,
} from '@elastic/charts';
-import { BUCKET_TYPES } from '../../../data/public';
-
import { Aspects } from '../types';
import './_detailed_tooltip.scss';
import { fillEmptyValue } from '../utils/get_series_name_fn';
-import { COMPLEX_SPLIT_ACCESSOR } from '../utils/accessors';
+import { COMPLEX_SPLIT_ACCESSOR, isRangeAggType } from '../utils/accessors';
interface TooltipData {
label: string;
value: string;
}
+// TODO: replace when exported from elastic/charts
+const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__';
+
const getTooltipData = (
aspects: Aspects,
header: TooltipValue | null,
@@ -37,10 +38,7 @@ const getTooltipData = (
const data: TooltipData[] = [];
if (header) {
- const xFormatter =
- aspects.x.aggType === BUCKET_TYPES.DATE_RANGE || aspects.x.aggType === BUCKET_TYPES.RANGE
- ? null
- : aspects.x.formatter;
+ const xFormatter = isRangeAggType(aspects.x.aggType) ? null : aspects.x.formatter;
data.push({
label: aspects.x.title,
value: xFormatter ? xFormatter(header.value) : `${header.value}`,
@@ -80,6 +78,28 @@ const getTooltipData = (
}
});
+ if (
+ aspects.splitColumn &&
+ valueSeries.smHorizontalAccessorValue !== undefined &&
+ valueSeries.smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE
+ ) {
+ data.push({
+ label: aspects.splitColumn.title,
+ value: `${valueSeries.smHorizontalAccessorValue}`,
+ });
+ }
+
+ if (
+ aspects.splitRow &&
+ valueSeries.smVerticalAccessorValue !== undefined &&
+ valueSeries.smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE
+ ) {
+ data.push({
+ label: aspects.splitRow.title,
+ value: `${valueSeries.smVerticalAccessorValue}`,
+ });
+ }
+
return data;
};
diff --git a/src/plugins/vis_type_xy/public/components/index.ts b/src/plugins/vis_type_xy/public/components/index.ts
index 260c08e0fc4a9..9b2559bafd18e 100644
--- a/src/plugins/vis_type_xy/public/components/index.ts
+++ b/src/plugins/vis_type_xy/public/components/index.ts
@@ -11,4 +11,3 @@ export { XYEndzones } from './xy_endzones';
export { XYCurrentTime } from './xy_current_time';
export { XYSettings } from './xy_settings';
export { XYThresholdLine } from './xy_threshold_line';
-export { SplitChartWarning } from './split_chart_warning';
diff --git a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx b/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx
deleted file mode 100644
index b708590e04479..0000000000000
--- a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx
+++ /dev/null
@@ -1,44 +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 React, { FC } from 'react';
-
-import { EuiLink, EuiCallOut } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-
-import { getDocLinks } from '../services';
-
-export const SplitChartWarning: FC = () => {
- const advancedSettingsLink = getDocLinks().links.management.visualizationSettings;
-
- return (
-
-
-
-
- ),
- }}
- />
-
- );
-};
diff --git a/src/plugins/vis_type_xy/public/config/get_aspects.ts b/src/plugins/vis_type_xy/public/config/get_aspects.ts
index b8da4386806d4..c031d3fa1fb9b 100644
--- a/src/plugins/vis_type_xy/public/config/get_aspects.ts
+++ b/src/plugins/vis_type_xy/public/config/get_aspects.ts
@@ -29,7 +29,10 @@ export function getEmptyAspect(): Aspect {
},
};
}
-export function getAspects(columns: DatatableColumn[], { x, y, z, series }: Dimensions): Aspects {
+export function getAspects(
+ columns: DatatableColumn[],
+ { x, y, z, series, splitColumn, splitRow }: Dimensions
+): Aspects {
const seriesDimensions = Array.isArray(series) || series === undefined ? series : [series];
return {
@@ -37,6 +40,8 @@ export function getAspects(columns: DatatableColumn[], { x, y, z, series }: Dime
y: getAspectsFromDimension(columns, y) ?? [],
z: z && z?.length > 0 ? getAspectsFromDimension(columns, z[0]) : undefined,
series: getAspectsFromDimension(columns, seriesDimensions),
+ splitColumn: splitColumn?.length ? getAspectsFromDimension(columns, splitColumn[0]) : undefined,
+ splitRow: splitRow?.length ? getAspectsFromDimension(columns, splitRow[0]) : undefined,
};
}
diff --git a/src/plugins/vis_type_xy/public/types/config.ts b/src/plugins/vis_type_xy/public/types/config.ts
index af3d840739f17..9d4660afa1634 100644
--- a/src/plugins/vis_type_xy/public/types/config.ts
+++ b/src/plugins/vis_type_xy/public/types/config.ts
@@ -43,6 +43,8 @@ export interface Aspects {
y: Aspect[];
z?: Aspect;
series?: Aspect[];
+ splitColumn?: Aspect;
+ splitRow?: Aspect;
}
export interface AxisGrid {
diff --git a/src/plugins/vis_type_xy/public/utils/accessors.tsx b/src/plugins/vis_type_xy/public/utils/accessors.tsx
index d1337251d36aa..e40248ae92e12 100644
--- a/src/plugins/vis_type_xy/public/utils/accessors.tsx
+++ b/src/plugins/vis_type_xy/public/utils/accessors.tsx
@@ -26,11 +26,15 @@ const getFieldName = (fieldName: string, index?: number) => {
return `${fieldName}${indexStr}`;
};
+export const isRangeAggType = (type: string | null) =>
+ type === BUCKET_TYPES.DATE_RANGE || type === BUCKET_TYPES.RANGE;
+
/**
* Returns accessor function for complex accessor types
* @param aspect
+ * @param isComplex - forces to be functional/complex accessor
*/
-export const getComplexAccessor = (fieldName: string) => (
+export const getComplexAccessor = (fieldName: string, isComplex: boolean = false) => (
aspect: Aspect,
index?: number
): Accessor | AccessorFn | undefined => {
@@ -38,12 +42,7 @@ export const getComplexAccessor = (fieldName: string) => (
return;
}
- if (
- !(
- (aspect.aggType === BUCKET_TYPES.DATE_RANGE || aspect.aggType === BUCKET_TYPES.RANGE) &&
- aspect.formatter
- )
- ) {
+ if (!((isComplex || isRangeAggType(aspect.aggType)) && aspect.formatter)) {
return aspect.accessor;
}
@@ -51,7 +50,7 @@ export const getComplexAccessor = (fieldName: string) => (
const accessor = aspect.accessor;
const fn: AccessorFn = (d) => {
const v = d[accessor];
- if (!v) {
+ if (v === undefined) {
return;
}
const f = formatter(v);
diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts b/src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts
new file mode 100644
index 0000000000000..393a6ee06cf58
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/utils/render_all_series.test.mocks.ts
@@ -0,0 +1,386 @@
+/*
+ * 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 { VisConfig } from '../types';
+
+export const getVisConfig = (): VisConfig => {
+ return {
+ markSizeRatio: 5.3999999999999995,
+ fittingFunction: 'linear',
+ detailedTooltip: true,
+ isTimeChart: true,
+ showCurrentTime: false,
+ showValueLabel: false,
+ enableHistogramMode: true,
+ tooltip: {
+ type: 'vertical',
+ },
+ aspects: {
+ x: {
+ accessor: 'col-0-2',
+ column: 0,
+ title: 'order_date per minute',
+ format: {
+ id: 'date',
+ params: {
+ pattern: 'HH:mm',
+ },
+ },
+ aggType: 'date_histogram',
+ aggId: '2',
+ params: {
+ date: true,
+ intervalESUnit: 'm',
+ intervalESValue: 1,
+ interval: 60000,
+ format: 'HH:mm',
+ },
+ },
+ y: [
+ {
+ accessor: 'col-1-3',
+ column: 1,
+ title: 'Average products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'avg',
+ aggId: '3',
+ params: {},
+ },
+ ],
+ },
+ xAxis: {
+ id: 'CategoryAxis-1',
+ position: 'bottom',
+ show: true,
+ style: {
+ axisTitle: {
+ visible: true,
+ },
+ tickLabel: {
+ visible: true,
+ rotation: 0,
+ },
+ },
+ groupId: 'CategoryAxis-1',
+ title: 'order_date per minute',
+ ticks: {
+ show: true,
+ showOverlappingLabels: false,
+ showDuplicates: false,
+ },
+ grid: {
+ show: false,
+ },
+ scale: {
+ type: 'time',
+ },
+ integersOnly: false,
+ },
+ yAxes: [
+ {
+ id: 'ValueAxis-1',
+ position: 'left',
+ show: true,
+ style: {
+ axisTitle: {
+ visible: true,
+ },
+ tickLabel: {
+ visible: true,
+ rotation: 0,
+ },
+ },
+ groupId: 'ValueAxis-1',
+ title: 'Percentiles of products.base_price',
+ ticks: {
+ show: true,
+ rotation: 0,
+ showOverlappingLabels: true,
+ showDuplicates: true,
+ },
+ grid: {
+ show: false,
+ },
+ scale: {
+ mode: 'normal',
+ type: 'linear',
+ },
+ domain: {},
+ integersOnly: false,
+ },
+ ],
+ legend: {
+ show: true,
+ position: 'right',
+ },
+ rotation: 0,
+ thresholdLine: {
+ color: '#E7664C',
+ show: false,
+ value: 10,
+ width: 1,
+ groupId: 'ValueAxis-1',
+ },
+ };
+};
+
+export const getVisConfigPercentiles = (): VisConfig => {
+ return {
+ markSizeRatio: 5.3999999999999995,
+ fittingFunction: 'linear',
+ detailedTooltip: true,
+ isTimeChart: true,
+ showCurrentTime: false,
+ showValueLabel: false,
+ enableHistogramMode: true,
+ tooltip: {
+ type: 'vertical',
+ },
+ aspects: {
+ x: {
+ accessor: 'col-0-2',
+ column: 0,
+ title: 'order_date per minute',
+ format: {
+ id: 'date',
+ params: {
+ pattern: 'HH:mm',
+ },
+ },
+ aggType: 'date_histogram',
+ aggId: '2',
+ params: {
+ date: true,
+ intervalESUnit: 'm',
+ intervalESValue: 1,
+ interval: 60000,
+ format: 'HH:mm',
+ },
+ },
+ y: [
+ {
+ accessor: 'col-1-3.1',
+ column: 1,
+ title: '1st percentile of products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'percentiles',
+ aggId: '3.1',
+ params: {},
+ },
+ {
+ accessor: 'col-2-3.5',
+ column: 2,
+ title: '5th percentile of products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'percentiles',
+ aggId: '3.5',
+ params: {},
+ },
+ {
+ accessor: 'col-3-3.25',
+ column: 3,
+ title: '25th percentile of products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'percentiles',
+ aggId: '3.25',
+ params: {},
+ },
+ {
+ accessor: 'col-4-3.50',
+ column: 4,
+ title: '50th percentile of products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'percentiles',
+ aggId: '3.50',
+ params: {},
+ },
+ {
+ accessor: 'col-5-3.75',
+ column: 5,
+ title: '75th percentile of products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'percentiles',
+ aggId: '3.75',
+ params: {},
+ },
+ {
+ accessor: 'col-6-3.95',
+ column: 6,
+ title: '95th percentile of products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'percentiles',
+ aggId: '3.95',
+ params: {},
+ },
+ {
+ accessor: 'col-7-3.99',
+ column: 7,
+ title: '99th percentile of products.base_price',
+ format: {
+ id: 'number',
+ },
+ aggType: 'percentiles',
+ aggId: '3.99',
+ params: {},
+ },
+ ],
+ },
+ xAxis: {
+ id: 'CategoryAxis-1',
+ position: 'bottom',
+ show: true,
+ style: {
+ axisTitle: {
+ visible: true,
+ },
+ tickLabel: {
+ visible: true,
+ rotation: 0,
+ },
+ },
+ groupId: 'CategoryAxis-1',
+ title: 'order_date per minute',
+ ticks: {
+ show: true,
+ showOverlappingLabels: false,
+ showDuplicates: false,
+ },
+ grid: {
+ show: false,
+ },
+ scale: {
+ type: 'time',
+ },
+ integersOnly: false,
+ },
+ yAxes: [
+ {
+ id: 'ValueAxis-1',
+ position: 'left',
+ show: true,
+ style: {
+ axisTitle: {
+ visible: true,
+ },
+ tickLabel: {
+ visible: true,
+ rotation: 0,
+ },
+ },
+ groupId: 'ValueAxis-1',
+ title: 'Percentiles of products.base_price',
+ ticks: {
+ show: true,
+ rotation: 0,
+ showOverlappingLabels: true,
+ showDuplicates: true,
+ },
+ grid: {
+ show: false,
+ },
+ scale: {
+ mode: 'normal',
+ type: 'linear',
+ },
+ domain: {},
+ integersOnly: false,
+ },
+ ],
+ legend: {
+ show: true,
+ position: 'right',
+ },
+ rotation: 0,
+ thresholdLine: {
+ color: '#E7664C',
+ show: false,
+ value: 10,
+ width: 1,
+ groupId: 'ValueAxis-1',
+ },
+ };
+};
+
+export const getPercentilesData = () => {
+ return [
+ {
+ 'col-0-2': 1610961900000,
+ 'col-1-3.1': 11.9921875,
+ 'col-2-3.5': 11.9921875,
+ 'col-3-3.25': 11.9921875,
+ 'col-4-3.50': 38.49609375,
+ 'col-5-3.75': 65,
+ 'col-6-3.95': 65,
+ 'col-7-3.99': 65,
+ },
+ {
+ 'col-0-2': 1610962980000,
+ 'col-1-3.1': 28.984375000000004,
+ 'col-2-3.5': 28.984375,
+ 'col-3-3.25': 28.984375,
+ 'col-4-3.50': 30.9921875,
+ 'col-5-3.75': 41.5,
+ 'col-6-3.95': 50,
+ 'col-7-3.99': 50,
+ },
+ {
+ 'col-0-2': 1610963280000,
+ 'col-1-3.1': 11.9921875,
+ 'col-2-3.5': 11.9921875,
+ 'col-3-3.25': 11.9921875,
+ 'col-4-3.50': 12.9921875,
+ 'col-5-3.75': 13.9921875,
+ 'col-6-3.95': 13.9921875,
+ 'col-7-3.99': 13.9921875,
+ },
+ {
+ 'col-0-2': 1610964180000,
+ 'col-1-3.1': 11.9921875,
+ 'col-2-3.5': 11.9921875,
+ 'col-3-3.25': 14.9921875,
+ 'col-4-3.50': 15.98828125,
+ 'col-5-3.75': 24.984375,
+ 'col-6-3.95': 85,
+ 'col-7-3.99': 85,
+ },
+ {
+ 'col-0-2': 1610964420000,
+ 'col-1-3.1': 11.9921875,
+ 'col-2-3.5': 11.9921875,
+ 'col-3-3.25': 11.9921875,
+ 'col-4-3.50': 23.99609375,
+ 'col-5-3.75': 42,
+ 'col-6-3.95': 42,
+ 'col-7-3.99': 42,
+ },
+ {
+ 'col-0-2': 1610964600000,
+ 'col-1-3.1': 10.9921875,
+ 'col-2-3.5': 10.992187500000002,
+ 'col-3-3.25': 10.9921875,
+ 'col-4-3.50': 12.4921875,
+ 'col-5-3.75': 13.9921875,
+ 'col-6-3.95': 13.9921875,
+ 'col-7-3.99': 13.9921875,
+ },
+ ];
+};
diff --git a/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx b/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx
new file mode 100644
index 0000000000000..d76ea49a2f110
--- /dev/null
+++ b/src/plugins/vis_type_xy/public/utils/render_all_series.test.tsx
@@ -0,0 +1,132 @@
+/*
+ * 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 { AreaSeries, BarSeries, CurveType } from '@elastic/charts';
+import { DatatableRow } from '../../../expressions/public';
+import { renderAllSeries } from './render_all_series';
+import {
+ getVisConfig,
+ getVisConfigPercentiles,
+ getPercentilesData,
+} from './render_all_series.test.mocks';
+import { SeriesParam, VisConfig } from '../types';
+
+const defaultSeriesParams = [
+ {
+ data: {
+ id: '3',
+ label: 'Label',
+ },
+ drawLinesBetweenPoints: true,
+ interpolate: 'linear',
+ lineWidth: 2,
+ mode: 'stacked',
+ show: true,
+ showCircles: true,
+ type: 'area',
+ valueAxis: 'ValueAxis-1',
+ },
+] as SeriesParam[];
+
+const defaultData = [
+ {
+ 'col-0-2': 1610960220000,
+ 'col-1-3': 26.984375,
+ },
+ {
+ 'col-0-2': 1610961300000,
+ 'col-1-3': 30.99609375,
+ },
+ {
+ 'col-0-2': 1610961900000,
+ 'col-1-3': 38.49609375,
+ },
+ {
+ 'col-0-2': 1610962980000,
+ 'col-1-3': 35.2421875,
+ },
+];
+
+describe('renderAllSeries', function () {
+ const getAllSeries = (visConfig: VisConfig, params: SeriesParam[], data: DatatableRow[]) => {
+ return renderAllSeries(
+ visConfig,
+ params,
+ data,
+ jest.fn(),
+ jest.fn(),
+ 'Europe/Athens',
+ 'col-0-2',
+ []
+ );
+ };
+
+ it('renders an area Series and not a bar series if type is area', () => {
+ const renderSeries = getAllSeries(getVisConfig(), defaultSeriesParams, defaultData);
+ const wrapper = shallow(
{renderSeries}
);
+ expect(wrapper.find(AreaSeries).length).toBe(1);
+ expect(wrapper.find(BarSeries).length).toBe(0);
+ });
+
+ it('renders a bar Series in case of histogram', () => {
+ const barSeriesParams = [{ ...defaultSeriesParams[0], type: 'histogram' }];
+
+ const renderBarSeries = renderAllSeries(
+ getVisConfig(),
+ barSeriesParams as SeriesParam[],
+ defaultData,
+ jest.fn(),
+ jest.fn(),
+ 'Europe/Athens',
+ 'col-0-2',
+ []
+ );
+ const wrapper = shallow(
);
diff --git a/src/plugins/vis_type_xy/public/vis_renderer.tsx b/src/plugins/vis_type_xy/public/vis_renderer.tsx
index 612388939d26b..1a47742b3d004 100644
--- a/src/plugins/vis_type_xy/public/vis_renderer.tsx
+++ b/src/plugins/vis_type_xy/public/vis_renderer.tsx
@@ -16,7 +16,6 @@ import { VisualizationContainer } from '../../visualizations/public';
import type { PersistedState } from '../../visualizations/public';
import { XyVisType } from '../common';
-import { SplitChartWarning } from './components/split_chart_warning';
import { VisComponentType } from './vis_component';
import { RenderValue, visName } from './xy_vis_fn';
@@ -36,24 +35,20 @@ export const xyVisRenderer: ExpressionRenderDefinition = {
reuseDomNode: true,
render: async (domNode, { visData, visConfig, visType, syncColors }, handlers) => {
const showNoResult = shouldShowNoResultsMessage(visData, visType);
- const isSplitChart = Boolean(visConfig.dimensions.splitRow);
handlers.onDestroy(() => unmountComponentAtNode(domNode));
render(
- <>
- {isSplitChart && }
-
-
-
- >
+
+
+ ,
domNode
);
diff --git a/src/plugins/vis_type_xy/public/vis_types/area.tsx b/src/plugins/vis_type_xy/public/vis_types/area.ts
similarity index 94%
rename from src/plugins/vis_type_xy/public/vis_types/area.tsx
rename to src/plugins/vis_type_xy/public/vis_types/area.ts
index 50721c349d6e9..09007a01ca8bc 100644
--- a/src/plugins/vis_type_xy/public/vis_types/area.tsx
+++ b/src/plugins/vis_type_xy/public/vis_types/area.ts
@@ -6,8 +6,6 @@
* Public License, v 1.
*/
-import React from 'react';
-
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
@@ -30,7 +28,6 @@ import { toExpressionAst } from '../to_ast';
import { ChartType } from '../../common';
import { getConfigCollections } from '../editor/collections';
import { getOptionTabs } from '../editor/common_config';
-import { SplitTooltip } from './split_tooltip';
export const getAreaVisTypeDefinition = (
showElasticChartsOptions = false
@@ -181,12 +178,6 @@ export const getAreaVisTypeDefinition = (
min: 0,
max: 1,
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
- // TODO: Remove when split chart aggs are supported
- // https://github.com/elastic/kibana/issues/82496
- ...(showElasticChartsOptions && {
- disabled: true,
- tooltip: ,
- }),
},
],
},
diff --git a/src/plugins/vis_type_xy/public/vis_types/histogram.tsx b/src/plugins/vis_type_xy/public/vis_types/histogram.ts
similarity index 94%
rename from src/plugins/vis_type_xy/public/vis_types/histogram.tsx
rename to src/plugins/vis_type_xy/public/vis_types/histogram.ts
index 4fc8dbbb80e7b..daae5f5e48e61 100644
--- a/src/plugins/vis_type_xy/public/vis_types/histogram.tsx
+++ b/src/plugins/vis_type_xy/public/vis_types/histogram.ts
@@ -6,8 +6,6 @@
* Public License, v 1.
*/
-import React from 'react';
-
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
@@ -30,7 +28,6 @@ import { ChartType } from '../../common';
import { getConfigCollections } from '../editor/collections';
import { getOptionTabs } from '../editor/common_config';
import { defaultCountLabel, LabelRotation } from '../../../charts/public';
-import { SplitTooltip } from './split_tooltip';
export const getHistogramVisTypeDefinition = (
showElasticChartsOptions = false
@@ -184,12 +181,6 @@ export const getHistogramVisTypeDefinition = (
min: 0,
max: 1,
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
- // TODO: Remove when split chart aggs are supported
- // https://github.com/elastic/kibana/issues/82496
- ...(showElasticChartsOptions && {
- disabled: true,
- tooltip: ,
- }),
},
],
},
diff --git a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts
similarity index 94%
rename from src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx
rename to src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts
index b53bb7bc9dd40..9e026fa0d7474 100644
--- a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx
+++ b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts
@@ -6,8 +6,6 @@
* Public License, v 1.
*/
-import React from 'react';
-
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
@@ -30,7 +28,6 @@ import { ChartType } from '../../common';
import { getConfigCollections } from '../editor/collections';
import { getOptionTabs } from '../editor/common_config';
import { defaultCountLabel, LabelRotation } from '../../../charts/public';
-import { SplitTooltip } from './split_tooltip';
export const getHorizontalBarVisTypeDefinition = (
showElasticChartsOptions = false
@@ -183,12 +180,6 @@ export const getHorizontalBarVisTypeDefinition = (
min: 0,
max: 1,
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
- // TODO: Remove when split chart aggs are supported
- // https://github.com/elastic/kibana/issues/82496
- ...(showElasticChartsOptions && {
- disabled: true,
- tooltip: ,
- }),
},
],
},
diff --git a/src/plugins/vis_type_xy/public/vis_types/line.tsx b/src/plugins/vis_type_xy/public/vis_types/line.ts
similarity index 94%
rename from src/plugins/vis_type_xy/public/vis_types/line.tsx
rename to src/plugins/vis_type_xy/public/vis_types/line.ts
index e9b0533b957f5..3f3087207fa19 100644
--- a/src/plugins/vis_type_xy/public/vis_types/line.tsx
+++ b/src/plugins/vis_type_xy/public/vis_types/line.ts
@@ -6,8 +6,6 @@
* Public License, v 1.
*/
-import React from 'react';
-
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
@@ -30,7 +28,6 @@ import { toExpressionAst } from '../to_ast';
import { ChartType } from '../../common';
import { getConfigCollections } from '../editor/collections';
import { getOptionTabs } from '../editor/common_config';
-import { SplitTooltip } from './split_tooltip';
export const getLineVisTypeDefinition = (
showElasticChartsOptions = false
@@ -175,12 +172,6 @@ export const getLineVisTypeDefinition = (
min: 0,
max: 1,
aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'],
- // TODO: Remove when split chart aggs are supported
- // https://github.com/elastic/kibana/issues/82496
- ...(showElasticChartsOptions && {
- disabled: true,
- tooltip: ,
- }),
},
],
},
diff --git a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx b/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx
deleted file mode 100644
index ca22136599341..0000000000000
--- a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx
+++ /dev/null
@@ -1,20 +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 React from 'react';
-
-import { FormattedMessage } from '@kbn/i18n/react';
-
-export function SplitTooltip() {
- return (
-
- );
-}
diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_type_xy/server/plugin.ts
index bd7957164fd1a..fa3dddfeca02a 100644
--- a/src/plugins/vis_type_xy/server/plugin.ts
+++ b/src/plugins/vis_type_xy/server/plugin.ts
@@ -24,8 +24,7 @@ export const uiSettingsConfig: Record> = {
description: i18n.translate(
'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.description',
{
- defaultMessage:
- 'Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation.',
+ defaultMessage: 'Enables legacy charts library for area, line and bar charts in visualize.',
}
),
category: ['visualization'],
diff --git a/src/plugins/visualizations/common/index.ts b/src/plugins/visualizations/common/index.ts
new file mode 100644
index 0000000000000..d4133eb9b7163
--- /dev/null
+++ b/src/plugins/visualizations/common/index.ts
@@ -0,0 +1,10 @@
+/*
+ * 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.
+ */
+
+/** @public types */
+export * from './types';
diff --git a/src/plugins/visualizations/common/types.ts b/src/plugins/visualizations/common/types.ts
new file mode 100644
index 0000000000000..4881b82a0e8d3
--- /dev/null
+++ b/src/plugins/visualizations/common/types.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 { SavedObjectAttributes } from 'kibana/server';
+import { AggConfigOptions } from 'src/plugins/data/common';
+
+export interface VisParams {
+ [key: string]: any;
+}
+
+export interface SavedVisState {
+ title: string;
+ type: string;
+ params: TVisParams;
+ aggs: AggConfigOptions[];
+}
+
+export interface VisualizationSavedObjectAttributes extends SavedObjectAttributes {
+ description: string;
+ kibanaSavedObjectMeta: {
+ searchSourceJSON: string;
+ };
+ title: string;
+ version: number;
+ visState: string;
+ uiStateJSON: string;
+}
diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts
index d1976cc84acec..0bf8aa6e5c418 100644
--- a/src/plugins/visualizations/public/index.ts
+++ b/src/plugins/visualizations/public/index.ts
@@ -34,7 +34,7 @@ export type {
Schema,
ISchemas,
} from './vis_types';
-export { VisParams, SerializedVis, SerializedVisData, VisData } from './vis';
+export { SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract;
export type VisualizeEmbeddableContract = PublicContract;
export { VisualizeInput } from './embeddable';
@@ -46,12 +46,13 @@ export { PersistedState } from './persisted_state';
export {
VisualizationControllerConstructor,
VisualizationController,
- SavedVisState,
ISavedVis,
VisSavedObject,
VisResponseValue,
VisToExpressionAst,
+ VisParams,
} from './types';
export { ExprVisAPIEvents } from './expressions/vis';
export { VisualizationListItem, VisualizationStage } from './vis_types/vis_type_alias_registry';
export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants';
+export { SavedVisState } from '../common';
diff --git a/src/plugins/visualizations/public/legacy/vis_update_state.d.ts b/src/plugins/visualizations/public/legacy/vis_update_state.d.ts
index f3643ad6adcbe..0d871b3b1dea4 100644
--- a/src/plugins/visualizations/public/legacy/vis_update_state.d.ts
+++ b/src/plugins/visualizations/public/legacy/vis_update_state.d.ts
@@ -6,7 +6,7 @@
* Public License, v 1.
*/
-import { SavedVisState } from '../types';
+import { SavedVisState } from '../../common';
declare function updateOldState(oldState: unknown): SavedVisState;
diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts b/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts
index c858306968ad8..a85a1d453a939 100644
--- a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts
+++ b/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references.test.ts
@@ -7,7 +7,8 @@
*/
import { extractReferences, injectReferences } from './saved_visualization_references';
-import { VisSavedObject, SavedVisState } from '../types';
+import { VisSavedObject } from '../types';
+import { SavedVisState } from '../../common';
describe('extractReferences', () => {
test('extracts nothing if savedSearchId is empty', () => {
diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts
index 2e57cd00486f7..dc9ca49840561 100644
--- a/src/plugins/visualizations/public/types.ts
+++ b/src/plugins/visualizations/public/types.ts
@@ -7,15 +7,12 @@
*/
import { SavedObject } from '../../../plugins/saved_objects/public';
-import {
- AggConfigOptions,
- SearchSourceFields,
- TimefilterContract,
-} from '../../../plugins/data/public';
+import { SearchSourceFields, TimefilterContract } from '../../../plugins/data/public';
import { ExpressionAstExpression } from '../../expressions/public';
-import { SerializedVis, Vis, VisParams } from './vis';
+import { SerializedVis, Vis } from './vis';
import { ExprVis } from './expressions/vis';
+import { SavedVisState, VisParams } from '../common/types';
export { Vis, SerializedVis, VisParams };
@@ -30,13 +27,6 @@ export type VisualizationControllerConstructor = new (
vis: ExprVis
) => VisualizationController;
-export interface SavedVisState {
- title: string;
- type: string;
- params: VisParams;
- aggs: AggConfigOptions[];
-}
-
export interface ISavedVis {
id?: string;
title: string;
diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts
index 58bcdb9ea49c6..56a151fb82ed3 100644
--- a/src/plugins/visualizations/public/vis.ts
+++ b/src/plugins/visualizations/public/vis.ts
@@ -30,6 +30,7 @@ import {
AggConfigOptions,
SearchSourceFields,
} from '../../../plugins/data/public';
+import { VisParams } from '../common/types';
export interface SerializedVisData {
expression?: string;
@@ -56,10 +57,6 @@ export interface VisData {
savedSearchId?: string;
}
-export interface VisParams {
- [key: string]: any;
-}
-
const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => {
const searchSource = inputSearchSource.createCopy();
if (savedSearchId) {
diff --git a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx
index a1b6aac30d813..b0d931c6c87fa 100644
--- a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx
+++ b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx
@@ -147,6 +147,10 @@ const TopNav = ({
}
return actions.default();
});
+ return () => {
+ // reset on app leave handler so leaving from the listing page doesn't trigger a confirmation
+ onAppLeave((actions) => actions.default());
+ };
}, [
onAppLeave,
originatingApp,
diff --git a/test/examples/config.js b/test/examples/config.js
index a720899a637de..aab71cb305016 100644
--- a/test/examples/config.js
+++ b/test/examples/config.js
@@ -19,6 +19,7 @@ export default async function ({ readConfigFile }) {
require.resolve('./ui_actions'),
require.resolve('./state_sync'),
require.resolve('./routing'),
+ require.resolve('./expressions_explorer'),
],
services: {
...functionalConfig.get('services'),
diff --git a/test/examples/expressions_explorer/expressions.ts b/test/examples/expressions_explorer/expressions.ts
new file mode 100644
index 0000000000000..7261564e6db38
--- /dev/null
+++ b/test/examples/expressions_explorer/expressions.ts
@@ -0,0 +1,44 @@
+/*
+ * 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 expect from '@kbn/expect';
+
+import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
+
+// eslint-disable-next-line import/no-default-export
+export default function ({ getService }: PluginFunctionalProviderContext) {
+ const testSubjects = getService('testSubjects');
+ const retry = getService('retry');
+ const browser = getService('browser');
+
+ describe('', () => {
+ it('runs expression', async () => {
+ await retry.try(async () => {
+ const text = await testSubjects.getVisibleText('expressionResult');
+ expect(text).to.be(
+ '{\n "type": "error",\n "error": {\n "message": "Function markdown could not be found.",\n "name": "fn not found"\n }\n}'
+ );
+ });
+ });
+
+ it('renders expression', async () => {
+ await retry.try(async () => {
+ const text = await testSubjects.getVisibleText('expressionRender');
+ expect(text).to.be('Function markdown could not be found.');
+ });
+ });
+
+ it('emits an action and navigates', async () => {
+ await testSubjects.click('testExpressionButton');
+ await retry.try(async () => {
+ const text = await browser.getCurrentUrl();
+ expect(text).to.be('https://www.google.com/?gws_rd=ssl');
+ });
+ });
+ });
+}
diff --git a/test/examples/expressions_explorer/index.ts b/test/examples/expressions_explorer/index.ts
new file mode 100644
index 0000000000000..77d2a594c0f29
--- /dev/null
+++ b/test/examples/expressions_explorer/index.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.
+ */
+
+import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
+
+// eslint-disable-next-line import/no-default-export
+export default function ({
+ getService,
+ getPageObjects,
+ loadTestFile,
+}: PluginFunctionalProviderContext) {
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['common', 'header']);
+
+ describe('expressions explorer', function () {
+ before(async () => {
+ await browser.setWindowSize(1300, 900);
+ await PageObjects.common.navigateToApp('expressionsExplorer');
+ });
+
+ loadTestFile(require.resolve('./expressions'));
+ });
+}
diff --git a/test/functional/apps/visualize/_inspector.ts b/test/functional/apps/visualize/_inspector.ts
index 07b5dfb8a769d..9d4623feef74a 100644
--- a/test/functional/apps/visualize/_inspector.ts
+++ b/test/functional/apps/visualize/_inspector.ts
@@ -6,12 +6,15 @@
* Public License, v 1.
*/
+import expect from '@kbn/expect';
+
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const log = getService('log');
const inspector = getService('inspector');
const filterBar = getService('filterBar');
+ const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']);
describe('inspector', function describeIndexTests() {
@@ -23,6 +26,34 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timePicker.setDefaultAbsoluteRange();
});
+ describe('advanced input JSON', () => {
+ it('should have "missing" property with value 10', async () => {
+ log.debug('Add Max Metric on memory field');
+ await PageObjects.visEditor.clickBucket('Y-axis', 'metrics');
+ await PageObjects.visEditor.selectAggregation('Max', 'metrics');
+ await PageObjects.visEditor.selectField('memory', 'metrics');
+
+ log.debug('Add value to advanced JSON input');
+ await PageObjects.visEditor.toggleAdvancedParams('2');
+ await testSubjects.setValue('codeEditorContainer', '{ "missing": 10 }');
+ await PageObjects.visEditor.clickGo();
+
+ await inspector.open();
+ await inspector.openInspectorRequestsView();
+ const requestTab = await inspector.getOpenRequestDetailRequestButton();
+ await requestTab.click();
+ const requestJSON = JSON.parse(await inspector.getCodeEditorValue());
+
+ expect(requestJSON.aggs['2'].max).property('missing', 10);
+ });
+
+ after(async () => {
+ await inspector.close();
+ await PageObjects.visEditor.removeDimension(2);
+ await PageObjects.visEditor.clickGo();
+ });
+ });
+
describe('inspector table', function indexPatternCreation() {
it('should update table header when columns change', async function () {
await inspector.open();
diff --git a/test/functional/apps/visualize/_line_chart_split_chart.ts b/test/functional/apps/visualize/_line_chart_split_chart.ts
index aeb80a58c9655..3e74bf0b7c0ec 100644
--- a/test/functional/apps/visualize/_line_chart_split_chart.ts
+++ b/test/functional/apps/visualize/_line_chart_split_chart.ts
@@ -176,8 +176,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false);
await PageObjects.visEditor.clickGo();
const labels = await PageObjects.visChart.getYAxisLabelsAsNumbers();
- const minLabel = 2;
- const maxLabel = 5000;
+ const minLabel = await PageObjects.visChart.getExpectedValue(2, 1);
+ const maxLabel = await PageObjects.visChart.getExpectedValue(5000, 7000);
const numberOfLabels = 10;
expect(labels.length).to.be.greaterThan(numberOfLabels);
expect(labels[0]).to.eql(minLabel);
@@ -188,8 +188,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true);
await PageObjects.visEditor.clickGo();
const labels = await PageObjects.visChart.getYAxisLabelsAsNumbers();
- const minLabel = 2;
- const maxLabel = 5000;
+ const minLabel = await PageObjects.visChart.getExpectedValue(2, 1);
+ const maxLabel = await PageObjects.visChart.getExpectedValue(5000, 7000);
const numberOfLabels = 10;
expect(labels.length).to.be.greaterThan(numberOfLabels);
expect(labels[0]).to.eql(minLabel);
@@ -201,7 +201,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false);
await PageObjects.visEditor.clickGo();
const labels = await PageObjects.visChart.getYAxisLabels();
- const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000'];
+ const expectedLabels = await PageObjects.visChart.getExpectedValue(
+ ['0', '2,000', '4,000', '6,000', '8,000', '10,000'],
+ ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000']
+ );
expect(labels).to.eql(expectedLabels);
});
@@ -210,7 +213,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true);
await PageObjects.visEditor.clickGo();
const labels = await PageObjects.visChart.getYAxisLabels();
- const expectedLabels = ['2,000', '4,000', '6,000', '8,000'];
+ const expectedLabels = await PageObjects.visChart.getExpectedValue(
+ ['2,000', '4,000', '6,000', '8,000'],
+ ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000']
+ );
expect(labels).to.eql(expectedLabels);
});
@@ -220,7 +226,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visEditor.clickGo();
const labels = await PageObjects.visChart.getYAxisLabels();
log.debug(labels);
- const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000'];
+ const expectedLabels = await PageObjects.visChart.getExpectedValue(
+ ['0', '2,000', '4,000', '6,000', '8,000', '10,000'],
+ ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000']
+ );
expect(labels).to.eql(expectedLabels);
});
@@ -228,7 +237,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true);
await PageObjects.visEditor.clickGo();
const labels = await PageObjects.visChart.getYAxisLabels();
- const expectedLabels = ['2,000', '4,000', '6,000', '8,000'];
+ const expectedLabels = await PageObjects.visChart.getExpectedValue(
+ ['2,000', '4,000', '6,000', '8,000'],
+ ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000']
+ );
expect(labels).to.eql(expectedLabels);
});
});
diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts
index dddcd82f1d3f8..8dd2854419693 100644
--- a/test/functional/apps/visualize/index.ts
+++ b/test/functional/apps/visualize/index.ts
@@ -52,6 +52,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
// Test replaced vislib chart types
loadTestFile(require.resolve('./_area_chart'));
loadTestFile(require.resolve('./_line_chart_split_series'));
+ loadTestFile(require.resolve('./_line_chart_split_chart'));
loadTestFile(require.resolve('./_point_series_options'));
loadTestFile(require.resolve('./_vertical_bar_chart'));
loadTestFile(require.resolve('./_vertical_bar_chart_nontimeindex'));
diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md
index 12c3ab12a6998..9472cbf400a6a 100644
--- a/x-pack/plugins/actions/README.md
+++ b/x-pack/plugins/actions/README.md
@@ -69,21 +69,26 @@ Table of Contents
- [`secrets`](#secrets-6)
- [`params`](#params-6)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice)
- - [`subActionParams (getFields)`](#subactionparams-getfields-1)
+ - [`subActionParams (getFields)`](#subactionparams-getfields)
- [Jira](#jira)
- [`config`](#config-7)
- [`secrets`](#secrets-7)
- [`params`](#params-7)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-1)
+ - [`subActionParams (getIncident)`](#subactionparams-getincident)
- [`subActionParams (issueTypes)`](#subactionparams-issuetypes)
- - [`subActionParams (getFields)`](#subactionparams-getfields-2)
- - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2)
+ - [`subActionParams (fieldsByIssueType)`](#subactionparams-fieldsbyissuetype)
+ - [`subActionParams (issues)`](#subactionparams-issues)
+ - [`subActionParams (issue)`](#subactionparams-issue)
+ - [`subActionParams (getFields)`](#subactionparams-getfields-1)
- [IBM Resilient](#ibm-resilient)
- [`config`](#config-8)
- [`secrets`](#secrets-8)
- [`params`](#params-8)
- - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-3)
- - [`subActionParams (getFields)`](#subactionparams-getfields-3)
+ - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2)
+ - [`subActionParams (getFields)`](#subactionparams-getfields-2)
+ - [`subActionParams (incidentTypes)`](#subactionparams-incidenttypes)
+ - [`subActionParams (severity)`](#subactionparams-severity)
- [Command Line Utility](#command-line-utility)
- [Developing New Action Types](#developing-new-action-types)
- [licensing](#licensing)
@@ -526,17 +531,17 @@ The PagerDuty action uses the [V2 Events API](https://v2.developer.pagerduty.com
### `params`
-| Property | Description | Type |
-| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
-| eventAction | One of `trigger` _(default)_, `resolve`, or `acknowlege`. See [event action](https://v2.developer.pagerduty.com/docs/events-api-v2#event-action) for more details. | string _(optional)_ |
-| dedupKey | All actions sharing this key will be associated with the same PagerDuty alert. Used to correlate trigger and resolution. The maximum length is **255** characters. See [alert deduplication](https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication) for details. | string _(optional)_ |
-| summary | A text summary of the event, defaults to `No summary provided`. The maximum length is **1024** characters. | string _(optional)_ |
-| source | The affected system, preferably a hostname or fully qualified domain name. Defaults to `Kibana Action `. | string _(optional)_ |
-| severity | The perceived severity of on the affected system. This can be one of `critical`, `error`, `warning` or `info`_(default)_. | string _(optional)_ |
-| timestamp | An [ISO-8601 format date-time](https://v2.developer.pagerduty.com/v2/docs/types#datetime), indicating the time the event was detected or generated. | string _(optional)_ |
-| component | The component of the source machine that is responsible for the event, for example `mysql` or `eth0`. | string _(optional)_ |
-| group | Logical grouping of components of a service, for example `app-stack`. | string _(optional)_ |
-| class | The class/type of the event, for example `ping failure` or `cpu load`. | string _(optional)_ |
+| Property | Description | Type |
+| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
+| eventAction | One of `trigger` _(default)_, `resolve`, or `acknowlege`. See [event action](https://v2.developer.pagerduty.com/docs/events-api-v2#event-action) for more details. | string _(optional)_ |
+| dedupKey | All actions sharing this key will be associated with the same PagerDuty alert. Used to correlate trigger and resolution. The maximum length is **255** characters. See [alert deduplication](https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication) for details. | string _(optional)_ |
+| summary | A text summary of the event, defaults to `No summary provided`. The maximum length is **1024** characters. | string _(optional)_ |
+| source | The affected system, preferably a hostname or fully qualified domain name. Defaults to `Kibana Action `. | string _(optional)_ |
+| severity | The perceived severity of on the affected system. This can be one of `critical`, `error`, `warning` or `info`_(default)_. | string _(optional)_ |
+| timestamp | An [ISO-8601 format date-time](https://v2.developer.pagerduty.com/v2/docs/types#datetime), indicating the time the event was detected or generated. | string _(optional)_ |
+| component | The component of the source machine that is responsible for the event, for example `mysql` or `eth0`. | string _(optional)_ |
+| group | Logical grouping of components of a service, for example `app-stack`. | string _(optional)_ |
+| class | The class/type of the event, for example `ping failure` or `cpu load`. | string _(optional)_ |
For more details see [PagerDuty v2 event parameters](https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2).
@@ -550,9 +555,9 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a
### `config`
-| Property | Description | Type |
-| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- |
-| apiUrl | ServiceNow instance URL. | string |
+| Property | Description | Type |
+| -------- | ------------------------ | ------ |
+| apiUrl | ServiceNow instance URL. | string |
### `secrets`
@@ -563,24 +568,28 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a
### `params`
-| Property | Description | Type |
-| --------------- | ------------------------------------------------------------------------------------ | ------ |
-| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, and `getIncident` | string |
-| subActionParams | The parameters of the sub action | object |
+| Property | Description | Type |
+| --------------- | --------------------------------------------------------------------- | ------ |
+| subAction | The sub action to perform. It can be `getFields`, and `pushToService` | string |
+| subActionParams | The parameters of the sub action | object |
#### `subActionParams (pushToService)`
-| Property | Description | Type |
-| ------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------- |
-| savedObjectId | The id of the saved object. | string |
-| title | The title of the incident. | string _(optional)_ |
-| description | The description of the incident. | string _(optional)_ |
-| comment | A comment. | string _(optional)_ |
-| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
-| externalId | The id of the incident in ServiceNow. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ |
-| severity | The name of the severity in ServiceNow. | string _(optional)_ |
-| urgency | The name of the urgency in ServiceNow. | string _(optional)_ |
-| impact | The name of the impact in ServiceNow. | string _(optional)_ |
+| Property | Description | Type |
+| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
+| incident | The ServiceNow incident. | object |
+| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
+
+The following table describes the properties of the `incident` object.
+
+| Property | Description | Type |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------- |
+| short_description | The title of the incident. | string |
+| description | The description of the incident. | string _(optional)_ |
+| externalId | The id of the incident in ServiceNow. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ |
+| severity | The name of the severity in ServiceNow. | string _(optional)_ |
+| urgency | The name of the urgency in ServiceNow. | string _(optional)_ |
+| impact | The name of the impact in ServiceNow. | string _(optional)_ |
#### `subActionParams (getFields)`
@@ -596,9 +605,9 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla
### `config`
-| Property | Description | Type |
-| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
-| apiUrl | Jira instance URL. | string |
+| Property | Description | Type |
+| -------- | ------------------ | ------ |
+| apiUrl | Jira instance URL. | string |
### `secrets`
@@ -609,48 +618,71 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla
### `params`
-| Property | Description | Type |
-| --------------- | ----------------------------------------------------------------------------------------------------------------------- | ------ |
-| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, `getIncident`, `issueTypes`, and `fieldsByIssueType` | string |
-| subActionParams | The parameters of the sub action | object |
+| Property | Description | Type |
+| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------ |
+| subAction | The sub action to perform. It can be `pushToService`, `getIncident`, `issueTypes`, `fieldsByIssueType`, `issues`, `issue`, and `getFields` | string |
+| subActionParams | The parameters of the sub action | object |
#### `subActionParams (pushToService)`
-| Property | Description | Type |
-| ------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- |
-| savedObjectId | The id of the saved object | string |
-| title | The title of the issue | string _(optional)_ |
-| description | The description of the issue | string _(optional)_ |
-| externalId | The id of the issue in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ |
-| issueType | The id of the issue type in Jira. | string _(optional)_ |
-| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ |
-| labels | An array of labels. | string[] _(optional)_ |
-| parent | The parent issue id or key. Only for `Sub-task` issue types. | string _(optional)_ |
-| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ |
+| Property | Description | Type |
+| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
+| incident | The Jira incident. | object |
+| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
-#### `subActionParams (issueTypes)`
+The following table describes the properties of the `incident` object.
-No parameters for `issueTypes` sub-action. Provide an empty object `{}`.
+| Property | Description | Type |
+| ----------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- |
+| summary | The title of the issue | string |
+| description | The description of the issue | string _(optional)_ |
+| externalId | The id of the issue in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ |
+| issueType | The id of the issue type in Jira. | string _(optional)_ |
+| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ |
+| labels | An array of labels. | string[] _(optional)_ |
+| parent | The parent issue id or key. Only for `Sub-task` issue types. | string _(optional)_ |
-#### `subActionParams (getFields)`
+#### `subActionParams (getIncident)`
-No parameters for `getFields` sub-action. Provide an empty object `{}`.
+| Property | Description | Type |
+| ---------- | --------------------------- | ------ |
+| externalId | The id of the issue in Jira | string |
-#### `subActionParams (pushToService)`
+#### `subActionParams (issueTypes)`
+
+No parameters for `issueTypes` sub-action. Provide an empty object `{}`.
+
+#### `subActionParams (fieldsByIssueType)`
| Property | Description | Type |
| -------- | -------------------------------- | ------ |
| id | The id of the issue type in Jira | string |
+#### `subActionParams (issues)`
+
+| Property | Description | Type |
+| -------- | ----------------------- | ------ |
+| title | The title to search for | string |
+
+#### `subActionParams (issue)`
+
+| Property | Description | Type |
+| -------- | --------------------------- | ------ |
+| id | The id of the issue in Jira | string |
+
+#### `subActionParams (getFields)`
+
+No parameters for `getFields` sub-action. Provide an empty object `{}`.
+
## IBM Resilient
ID: `.resilient`
### `config`
-| Property | Description | Type |
-| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
-| apiUrl | IBM Resilient instance URL. | string |
+| Property | Description | Type |
+| -------- | --------------------------- | ------ |
+| apiUrl | IBM Resilient instance URL. | string |
### `secrets`
@@ -661,19 +693,24 @@ ID: `.resilient`
### `params`
-| Property | Description | Type |
-| --------------- | ------------------------------------------------------------------------------------ | ------ |
-| subAction | The sub action to perform. It can be `getFields`, `pushToService`, `handshake`, and `getIncident` | string |
-| subActionParams | The parameters of the sub action | object |
+| Property | Description | Type |
+| --------------- | -------------------------------------------------------------------------------------------------- | ------ |
+| subAction | The sub action to perform. It can be `pushToService`, `getFields`, `incidentTypes`, and `severity` | string |
+| subActionParams | The parameters of the sub action | object |
#### `subActionParams (pushToService)`
+| Property | Description | Type |
+| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
+| incident | The IBM Resilient incident. | object |
+| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
+
+The following table describes the properties of the `incident` object.
+
| Property | Description | Type |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------- |
-| savedObjectId | The id of the saved object | string |
-| title | The title of the incident | string _(optional)_ |
+| name | The title of the incident | string _(optional)_ |
| description | The description of the incident | string _(optional)_ |
-| comments | The comments of the incident. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ |
| externalId | The id of the incident in IBM Resilient. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ |
| incidentTypes | An array with the ids of IBM Resilient incident types. | number[] _(optional)_ |
| severityCode | IBM Resilient id of the severity code. | number _(optional)_ |
@@ -682,6 +719,14 @@ ID: `.resilient`
No parameters for `getFields` sub-action. Provide an empty object `{}`.
+#### `subActionParams (incidentTypes)`
+
+No parameters for `incidentTypes` sub-action. Provide an empty object `{}`.
+
+#### `subActionParams (severity)`
+
+No parameters for `severity` sub-action. Provide an empty object `{}`.
+
# Command Line Utility
The [`kbn-action`](https://github.com/pmuellr/kbn-action) tool can be used to send HTTP requests to the Actions plugin. For instance, to create a Slack action from the `.slack` Action Type, use the following command:
diff --git a/x-pack/plugins/apm/common/service_health_status.ts b/x-pack/plugins/apm/common/service_health_status.ts
index f66e03a9733a3..c2ba42cc20760 100644
--- a/x-pack/plugins/apm/common/service_health_status.ts
+++ b/x-pack/plugins/apm/common/service_health_status.ts
@@ -5,10 +5,9 @@
*/
import { i18n } from '@kbn/i18n';
+import { EuiTheme } from '../../../../src/plugins/kibana_react/common';
import { ANOMALY_SEVERITY } from '../../ml/common';
-import { EuiTheme } from '../../xpack_legacy/common';
-
export enum ServiceHealthStatus {
healthy = 'healthy',
critical = 'critical',
diff --git a/x-pack/plugins/apm/common/ui_settings_keys.ts b/x-pack/plugins/apm/common/ui_settings_keys.ts
index ffc2a2ef21fe9..38922fa445a47 100644
--- a/x-pack/plugins/apm/common/ui_settings_keys.ts
+++ b/x-pack/plugins/apm/common/ui_settings_keys.ts
@@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export const enableSignificantTerms = 'apm:enableSignificantTerms';
+export const enableCorrelations = 'apm:enableCorrelations';
export const enableServiceOverview = 'apm:enableServiceOverview';
diff --git a/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx b/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx
index b2d88c4c3849b..438303110fbc4 100644
--- a/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx
+++ b/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx
@@ -128,7 +128,7 @@ export function LatencyCorrelations() {
- View significant terms
+ View correlations
@@ -62,7 +62,7 @@ export function Correlations() {
>
-
Significant terms
+
Correlations
@@ -88,7 +88,7 @@ export function Correlations() {
iconType="alert"
>
- Significant terms is an experimental feature and in active
+ Correlations is an experimental feature and in active
development. Bugs and surprises are to be expected but let us
know your feedback so we can improve it.
diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx
index ff95d6fd1254c..a7b0b0393ea94 100644
--- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.stories.tsx
@@ -5,7 +5,7 @@
*/
import React, { ComponentType } from 'react';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
import { Exception } from '../../../../../typings/es_schemas/raw/error_raw';
import { ExceptionStacktrace } from './ExceptionStacktrace';
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx
index 392b42cba12e5..29d5750231762 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useContext } from 'react';
+import React, { useContext, useMemo } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
@@ -17,7 +17,7 @@ import { I18LABELS } from '../translations';
import { KeyUXMetrics } from './KeyUXMetrics';
import { useFetcher } from '../../../../hooks/use_fetcher';
import { useUxQuery } from '../hooks/useUxQuery';
-import { CoreVitals } from '../../../../../../observability/public';
+import { getCoreVitalsComponent } from '../../../../../../observability/public';
import { CsmSharedContext } from '../CsmSharedContext';
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
import { getPercentileLabel } from './translations';
@@ -48,6 +48,18 @@ export function UXMetrics() {
sharedData: { totalPageViews },
} = useContext(CsmSharedContext);
+ const CoreVitals = useMemo(
+ () =>
+ getCoreVitalsComponent({
+ data,
+ totalPageViews,
+ loading: status !== 'success',
+ displayTrafficMetric: true,
+ }),
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [status]
+ );
+
return (
@@ -67,12 +79,7 @@ export function UXMetrics() {
-
+ {CoreVitals}
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx
index 1053dd611d519..50615c61dea1f 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx
@@ -6,7 +6,7 @@
import { storiesOf } from '@storybook/react';
import React from 'react';
-import { EuiThemeProvider } from '../../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common';
import { MapToolTip } from '../MapToolTip';
import { COUNTRY_NAME, TRANSACTION_DURATION_COUNTRY } from '../useLayerList';
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx
index d5b8cd83d437c..ef44746ffd3f5 100644
--- a/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx
+++ b/x-pack/plugins/apm/public/components/app/RumDashboard/utils/test_helper.tsx
@@ -11,7 +11,7 @@ import { of } from 'rxjs';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { MemoryHistory } from 'history';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public';
import { UrlParamsProvider } from '../../../../context/url_params_context/url_params_context';
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx
index 313b262508c61..b36cb363f4c25 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx
@@ -7,7 +7,7 @@
import cytoscape from 'cytoscape';
import { HttpSetup } from 'kibana/public';
import React, { ComponentType } from 'react';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context';
import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider';
import { createCallApmApi } from '../../../../services/rest/createCallApmApi';
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx
index 052f9e9515751..4212cdd29e81a 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/service_stats_list.stories.tsx
@@ -5,7 +5,7 @@
*/
import React, { ComponentType } from 'react';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
import { ServiceStatsList } from './ServiceStatsList';
export default {
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx
index a4985d2f5ab0c..e66eeb4759d50 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/Cytoscape.stories.tsx
@@ -7,7 +7,7 @@
import { EuiCard, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import cytoscape from 'cytoscape';
import React, { ComponentType } from 'react';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
import { Cytoscape } from '../Cytoscape';
import { iconForNode } from '../icons';
import { Centerer } from './centerer';
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx
index 0673735ba0adb..349d60f481bac 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/cytoscape_example_data.stories.tsx
@@ -16,7 +16,7 @@ import {
EuiToolTip,
} from '@elastic/eui';
import React, { ComponentType, useEffect, useState } from 'react';
-import { EuiThemeProvider } from '../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common';
import { Cytoscape } from '../Cytoscape';
import { Centerer } from './centerer';
import exampleResponseHipsterStore from './example_response_hipster_store.json';
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts
index 9a0ebb7173c26..a1e49876d8291 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts
@@ -5,7 +5,7 @@
*/
import cytoscape from 'cytoscape';
import { CSSProperties } from 'react';
-import { EuiTheme } from '../../../../../observability/public';
+import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common';
import { ServiceAnomalyStats } from '../../../../common/anomaly_detection';
import {
SERVICE_NAME,
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx
index c6f82e3492750..7980543e41b16 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx
@@ -8,9 +8,9 @@ import { render } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { CoreStart } from 'kibana/public';
import React, { ReactNode } from 'react';
-import { createKibanaReactContext } from 'src/plugins/kibana_react/public';
+import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public';
import { License } from '../../../../../licensing/common/license';
-import { EuiThemeProvider } from '../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context';
import { LicenseContext } from '../../../context/license/license_context';
import * as useFetcherModule from '../../../hooks/use_fetcher';
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx
index ab16da1410662..767186781aff2 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.test.tsx
@@ -7,7 +7,8 @@
import { renderHook } from '@testing-library/react-hooks';
import cytoscape from 'cytoscape';
import dagre from 'cytoscape-dagre';
-import { EuiTheme, useUiTracker } from '../../../../../observability/public';
+import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common';
+import { useUiTracker } from '../../../../../observability/public';
import { useCytoscapeEventHandlers } from './use_cytoscape_event_handlers';
import lodash from 'lodash';
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts
index a2b229780ead0..cf7ee50f242b9 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/use_cytoscape_event_handlers.ts
@@ -7,7 +7,8 @@
import cytoscape from 'cytoscape';
import { debounce } from 'lodash';
import { useEffect } from 'react';
-import { EuiTheme, useUiTracker } from '../../../../../observability/public';
+import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common';
+import { useUiTracker } from '../../../../../observability/public';
import { getAnimationOptions, getNodeHeight } from './cytoscape_options';
/*
diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx
index 5ca643428e49c..a03b0b255e9f1 100644
--- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx
@@ -13,6 +13,7 @@
import { storiesOf } from '@storybook/react';
import React from 'react';
import { HttpSetup } from 'kibana/public';
+import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common';
import { AgentConfiguration } from '../../../../../../common/agent_configuration/configuration_types';
import { FETCH_STATUS } from '../../../../../hooks/use_fetcher';
import { createCallApmApi } from '../../../../../services/rest/createCallApmApi';
@@ -21,7 +22,6 @@ import {
ApmPluginContext,
ApmPluginContextValue,
} from '../../../../../context/apm_plugin/apm_plugin_context';
-import { EuiThemeProvider } from '../../../../../../../observability/public';
storiesOf(
'app/Settings/AgentConfigurations/AgentConfigurationCreateEdit',
diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx
index a94c48f02c101..b7806774536fa 100644
--- a/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx
@@ -23,10 +23,10 @@ import d3 from 'd3';
import { isEmpty } from 'lodash';
import React from 'react';
import { ValuesType } from 'utility-types';
-import { useTheme } from '../../../../../../observability/public';
import { getDurationFormatter } from '../../../../../common/utils/formatters';
import type { IUrlParams } from '../../../../context/url_params_context/types';
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
+import { useTheme } from '../../../../hooks/use_theme';
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import { unit } from '../../../../style/variables';
import { ChartContainer } from '../../../shared/charts/chart_container';
diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
index 5217c2abb11dc..6e78d30bfa1d9 100644
--- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
+++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
@@ -6,7 +6,7 @@
import React, { ComponentType } from 'react';
import { MemoryRouter } from 'react-router-dom';
-import { EuiThemeProvider } from '../../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { TraceAPIResponse } from '../../../../../../server/lib/traces/get_trace';
import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context';
diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx
index eb8068bc8114d..7c2729dbb0a16 100644
--- a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx
@@ -9,10 +9,10 @@ import { CoreStart } from 'kibana/public';
import { merge } from 'lodash';
import React, { ReactNode } from 'react';
import { MemoryRouter } from 'react-router-dom';
-import { createKibanaReactContext } from 'src/plugins/kibana_react/public';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
+import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public';
import { ServiceHealthStatus } from '../../../../common/service_health_status';
import { ServiceInventory } from '.';
-import { EuiThemeProvider } from '../../../../../observability/public';
import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context';
import {
mockApmPluginContextValue,
diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx
index 072b5b5bc7a96..aaaaf5174e4fd 100644
--- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx
+++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.test.tsx
@@ -8,7 +8,7 @@ import { shallow } from 'enzyme';
import React from 'react';
import { AgentMarker } from './AgentMarker';
import { AgentMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks';
-import { EuiThemeProvider } from '../../../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common';
describe('AgentMarker', () => {
const mark = {
diff --git a/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx b/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx
index 6348097a3e3ad..6790301c13abf 100644
--- a/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx
+++ b/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx
@@ -6,7 +6,7 @@
import { render } from '@testing-library/react';
import React, { ReactNode } from 'react';
import { MemoryRouter } from 'react-router-dom';
-import { EuiThemeProvider } from '../../../../../observability/public';
+import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common';
import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider';
import { IUrlParams } from '../../../context/url_params_context/types';
import {
diff --git a/x-pack/plugins/apm/public/hooks/use_theme.tsx b/x-pack/plugins/apm/public/hooks/use_theme.tsx
index e372a764a9505..3c50424b219dd 100644
--- a/x-pack/plugins/apm/public/hooks/use_theme.tsx
+++ b/x-pack/plugins/apm/public/hooks/use_theme.tsx
@@ -6,7 +6,7 @@
import { useContext } from 'react';
import { ThemeContext } from 'styled-components';
-import { EuiTheme } from '../../../observability/public';
+import { EuiTheme } from '../../../../../src/plugins/kibana_react/common';
export function useTheme(): EuiTheme {
const theme = useContext(ThemeContext);
diff --git a/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts b/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts
index 7b0826fa76883..7214d0b99bd6a 100644
--- a/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts
+++ b/x-pack/plugins/apm/public/selectors/latency_chart_selector.test.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiTheme } from '../../../xpack_legacy/common';
+import { EuiTheme } from '../../../../../src/plugins/kibana_react/common';
import { LatencyAggregationType } from '../../common/latency_aggregation_types';
import {
getLatencyChartSelector,
diff --git a/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts
index dba698ffb1bc1..8ef57ac74be66 100644
--- a/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts
+++ b/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts
@@ -6,7 +6,7 @@
import { Fit } from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import { rgba } from 'polished';
-import { EuiTheme } from '../../../observability/public';
+import { EuiTheme } from '../../../../../src/plugins/kibana_react/common';
import { asDuration } from '../../common/utils/formatters';
import { APMChartSpec, Coordinate } from '../../typings/timeseries';
import { APIReturnType } from '../services/rest/createCallApmApi';
diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts
index 03877b9e5bff2..e5fed2ee2ae22 100644
--- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts
+++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiTheme } from '../../../observability/public';
+import { EuiTheme } from '../../../../../src/plugins/kibana_react/common';
import {
getThrouputChartSelector,
ThrouputChartsResponse,
diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts
index a392f247aec42..b21fcb99fbe1a 100644
--- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts
+++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts
@@ -5,7 +5,7 @@
*/
import { difference, zipObject } from 'lodash';
-import { EuiTheme } from '../../../observability/public';
+import { EuiTheme } from '../../../../../src/plugins/kibana_react/common';
import { asTransactionRate } from '../../common/utils/formatters';
import { TimeSeries } from '../../typings/timeseries';
import { APIReturnType } from '../services/rest/createCallApmApi';
diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx
index 21c87c18be363..98745b4f6c636 100644
--- a/x-pack/plugins/apm/public/utils/testHelpers.tsx
+++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx
@@ -14,12 +14,12 @@ import moment from 'moment';
import { Moment } from 'moment-timezone';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
+import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
import {
ESFilter,
ESSearchRequest,
ESSearchResponse,
} from '../../../../typings/elasticsearch';
-import { EuiThemeProvider } from '../../../observability/public';
import { PromiseReturnType } from '../../../observability/typings/common';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { APMConfig } from '../../server';
diff --git a/x-pack/plugins/apm/server/ui_settings.ts b/x-pack/plugins/apm/server/ui_settings.ts
index c86fb636b5a1a..e9bb747280fc7 100644
--- a/x-pack/plugins/apm/server/ui_settings.ts
+++ b/x-pack/plugins/apm/server/ui_settings.ts
@@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
import { UiSettingsParams } from '../../../../src/core/types';
import {
- enableSignificantTerms,
+ enableCorrelations,
enableServiceOverview,
} from '../common/ui_settings_keys';
@@ -16,17 +16,16 @@ import {
* uiSettings definitions for APM.
*/
export const uiSettings: Record> = {
- [enableSignificantTerms]: {
+ [enableCorrelations]: {
category: ['observability'],
name: i18n.translate('xpack.apm.enableCorrelationsExperimentName', {
- defaultMessage: 'APM Significant terms (Platinum required)',
+ defaultMessage: 'APM correlations (Platinum required)',
}),
value: false,
description: i18n.translate(
'xpack.apm.enableCorrelationsExperimentDescription',
{
- defaultMessage:
- 'Enable the experimental Significant terms feature in APM',
+ defaultMessage: 'Enable the experimental correlations feature in APM',
}
),
schema: schema.boolean(),
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx
index b4fbba96e8dfb..341913a033c05 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx
+++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx
@@ -26,9 +26,11 @@ export const debug: RendererFactory = () => ({
ReactDOM.render(renderDebug(), domNode, () => handlers.done());
- handlers.onResize(() => {
- ReactDOM.render(renderDebug(), domNode, () => handlers.done());
- });
+ if (handlers.onResize) {
+ handlers.onResize(() => {
+ ReactDOM.render(renderDebug(), domNode, () => handlers.done());
+ });
+ }
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
},
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot
index 60fd7f43044dc..c2f9d88724609 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot
+++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot
@@ -63,6 +63,7 @@ exports[`Storyshots arguments/AxisConfig extended 1`] = `
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
@@ -139,6 +140,7 @@ exports[`Storyshots arguments/AxisConfig/components extended 1`] = `
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot
index 8f0a8b5abd9b5..238fe7c259c6e 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot
+++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/__stories__/__snapshots__/date_format.stories.storyshot
@@ -46,6 +46,7 @@ Array [
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
@@ -118,6 +119,7 @@ Array [
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
@@ -189,6 +191,7 @@ exports[`Storyshots arguments/DateFormat with preset format 1`] = `
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot
index 17f158c680a67..2159e49e2bcf1 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot
+++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/__stories__/__snapshots__/number_format.stories.storyshot
@@ -56,6 +56,7 @@ Array [
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
@@ -138,6 +139,7 @@ Array [
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
@@ -219,6 +221,7 @@ exports[`Storyshots arguments/NumberFormat with preset format 1`] = `
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
+ size="s"
/>
diff --git a/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot b/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot
index 900665fc1ddec..b833120f84f39 100644
--- a/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot
+++ b/x-pack/plugins/canvas/public/components/saved_elements_modal/__stories__/__snapshots__/saved_elements_modal.stories.storyshot
@@ -75,6 +75,7 @@ exports[`Storyshots components/SavedElementsModal no custom elements 1`] = `
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="search"
+ size="m"
/>
@@ -222,6 +223,7 @@ exports[`Storyshots components/SavedElementsModal with custom elements 1`] = `
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="search"
+ size="m"
/>
@@ -669,6 +671,7 @@ exports[`Storyshots components/SavedElementsModal with text filter 1`] = `
aria-hidden="true"
className="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="search"
+ size="m"
/>
diff --git a/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot b/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot
index 6f88120fb2b84..f21ffcf1a70ea 100644
--- a/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot
+++ b/x-pack/plugins/canvas/public/components/tag/__stories__/__snapshots__/tag.stories.storyshot
@@ -46,7 +46,7 @@ exports[`Storyshots components/Tags/Tag as badge with color 1`] = `
exports[`Storyshots components/Tags/Tag as health 1`] = `